From f2b1184805d839ec660b43f99d6c5e291404239f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 22 Sep 2022 23:11:38 +0800 Subject: [PATCH 001/116] Remove need for smartstring/std. --- CHANGELOG.md | 2 +- src/api/files.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adab88b9..98281abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Bug fixes --------- * Compiling on 32-bit architectures no longer cause a compilation error. -* Fix type-size test fo 32-bit architectures without the `decimal` feature. +* Fix type-size test for 32-bit architectures without the `decimal` feature. Custom syntax with state ------------------------ diff --git a/src/api/files.rs b/src/api/files.rs index ad2bbeae..b2a2ffd1 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -108,7 +108,7 @@ impl Engine { pub fn compile_file_with_scope(&self, scope: &Scope, path: PathBuf) -> RhaiResultOf { Self::read_file(&path).and_then(|contents| { let mut ast = self.compile_with_scope(scope, &contents)?; - ast.set_source(path.to_string_lossy()); + ast.set_source(path.to_string_lossy().as_ref()); Ok(ast) }) } From def1a683efa1274a1d106ffef46790dd54f69f41 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 24 Sep 2022 16:59:01 +0800 Subject: [PATCH 002/116] Distinguish string literals from identifiers in custom syntax parser. --- CHANGELOG.md | 8 ++++++++ src/parser.rs | 2 +- src/tokenizer.rs | 2 +- tests/custom_syntax.rs | 28 ++++++++++++++++------------ 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98281abf..a057eebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Rhai Release Notes ================== +Version 1.11.0 +============== + +Enhancements + +* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers. + + Version 1.10.1 ============== diff --git a/src/parser.rs b/src/parser.rs index 20600e0b..621a1b47 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2461,7 +2461,7 @@ impl Engine { use crate::api::custom_syntax::markers::*; let mut settings = settings; - let mut inputs = StaticVec::::new(); + let mut inputs = StaticVec::new_const(); let mut segments = StaticVec::new_const(); let mut tokens = StaticVec::new_const(); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 71305dec..6cc7eb57 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -699,7 +699,7 @@ impl Token { FloatConstant(f) => f.to_string().into(), #[cfg(feature = "decimal")] DecimalConstant(d) => d.to_string().into(), - StringConstant(..) => "string".into(), + StringConstant(s) => format!("\"{s}\"").into(), InterpolatedString(..) => "string".into(), CharConstant(c) => c.to_string().into(), Identifier(s) => s.to_string().into(), diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index 2c8db7f1..dacca728 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -256,10 +256,18 @@ fn test_custom_syntax_raw() -> Result<(), Box> { engine.register_custom_syntax_with_state_raw( "hello", - |stream, _, state| match stream.len() { + |stream, look_ahead, state| match stream.len() { 0 => unreachable!(), - 1 => Ok(Some("$ident$".into())), + 1 if look_ahead == "\"world\"" => { + *state = Dynamic::TRUE; + Ok(Some("$string$".into())) + } + 1 => { + *state = Dynamic::FALSE; + Ok(Some("$ident$".into())) + } 2 => match stream[1].as_str() { + "world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())), "world" => Ok(Some("$$hello".into())), "kitty" => { *state = (42 as INT).into(); @@ -276,16 +284,11 @@ fn test_custom_syntax_raw() -> Result<(), Box> { context.scope_mut().push("foo", 999 as INT); Ok(match inputs[0].get_string_value().unwrap() { - "world" - if inputs - .last() - .unwrap() - .get_string_value() - .map_or(false, |s| s == "$$hello") => - { - 0 as INT - } - "world" => 123 as INT, + "world" => match inputs.last().unwrap().get_string_value().unwrap_or("") { + "$$hello" => 0 as INT, + "$$world" => 123456 as INT, + _ => 123 as INT, + }, "kitty" if inputs.len() > 1 => 999 as INT, "kitty" => state.as_int().unwrap(), _ => unreachable!(), @@ -294,6 +297,7 @@ fn test_custom_syntax_raw() -> Result<(), Box> { }, ); + assert_eq!(engine.eval::(r#"hello "world""#)?, 123456); assert_eq!(engine.eval::("hello world")?, 0); assert_eq!(engine.eval::("hello kitty")?, 42); assert_eq!( From b56a9c22f3eb90ca89d788ba013a3a41bb4fb19c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Sep 2022 12:24:03 +0800 Subject: [PATCH 003/116] Refactor. --- src/api/custom_syntax.rs | 2 ++ src/api/mod.rs | 1 + src/api/register.rs | 2 ++ src/ast/ast.rs | 3 +++ src/ast/expr.rs | 4 ++++ src/ast/ident.rs | 3 +++ src/ast/namespace.rs | 2 ++ src/ast/stmt.rs | 4 ++++ src/engine.rs | 6 +++--- src/eval/target.rs | 4 ++++ src/module/resolvers/file.rs | 4 ++-- src/types/dynamic.rs | 2 ++ src/types/error.rs | 2 ++ src/types/immutable_string.rs | 8 +++++++- src/types/parse_error.rs | 10 +++++++--- tests/operations.rs | 6 +++--- tests/plugins.rs | 2 +- tests/print.rs | 2 +- tests/serde.rs | 2 +- tests/var_scope.rs | 2 +- 20 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index ad7e5b40..080d4477 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -147,6 +147,7 @@ impl Expression<'_> { impl AsRef for Expression<'_> { #[inline(always)] + #[must_use] fn as_ref(&self) -> &Expr { self.0 } @@ -156,6 +157,7 @@ impl Deref for Expression<'_> { type Target = Expr; #[inline(always)] + #[must_use] fn deref(&self) -> &Self::Target { self.0 } diff --git a/src/api/mod.rs b/src/api/mod.rs index e9e03854..6ddf090b 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -75,6 +75,7 @@ impl Engine { /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub fn module_resolver(&self) -> &dyn crate::ModuleResolver { &*self.module_resolver } diff --git a/src/api/register.rs b/src/api/register.rs index 2b4e20ed..9095a31f 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -16,12 +16,14 @@ 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") diff --git a/src/ast/ast.rs b/src/ast/ast.rs index da0454fe..902a6856 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -919,6 +919,7 @@ impl> AddAssign for AST { impl AsRef<[Stmt]> for AST { #[inline(always)] + #[must_use] fn as_ref(&self) -> &[Stmt] { self.statements() } @@ -927,6 +928,7 @@ impl AsRef<[Stmt]> for AST { #[cfg(not(feature = "no_function"))] impl AsRef for AST { #[inline(always)] + #[must_use] fn as_ref(&self) -> &crate::Module { self.shared_lib().as_ref() } @@ -935,6 +937,7 @@ impl AsRef for AST { #[cfg(not(feature = "no_function"))] impl AsRef> for AST { #[inline(always)] + #[must_use] fn as_ref(&self) -> &crate::Shared { self.shared_lib() } diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 3b07ecfd..389ac137 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -257,6 +257,7 @@ impl Hash for FloatWrapper { #[cfg(not(feature = "no_float"))] impl AsRef for FloatWrapper { #[inline(always)] + #[must_use] fn as_ref(&self) -> &F { &self.0 } @@ -265,6 +266,7 @@ impl AsRef for FloatWrapper { #[cfg(not(feature = "no_float"))] impl AsMut for FloatWrapper { #[inline(always)] + #[must_use] fn as_mut(&mut self) -> &mut F { &mut self.0 } @@ -275,6 +277,7 @@ impl Deref for FloatWrapper { type Target = F; #[inline(always)] + #[must_use] fn deref(&self) -> &Self::Target { &self.0 } @@ -283,6 +286,7 @@ impl Deref for FloatWrapper { #[cfg(not(feature = "no_float"))] impl DerefMut for FloatWrapper { #[inline(always)] + #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/src/ast/ident.rs b/src/ast/ident.rs index 13ab4ef9..8a6d98aa 100644 --- a/src/ast/ident.rs +++ b/src/ast/ident.rs @@ -28,6 +28,7 @@ impl fmt::Debug for Ident { impl AsRef for Ident { #[inline(always)] + #[must_use] fn as_ref(&self) -> &str { self.name.as_ref() } @@ -37,6 +38,7 @@ impl Deref for Ident { type Target = ImmutableString; #[inline(always)] + #[must_use] fn deref(&self) -> &Self::Target { &self.name } @@ -44,6 +46,7 @@ impl Deref for Ident { impl DerefMut for Ident { #[inline(always)] + #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.name } diff --git a/src/ast/namespace.rs b/src/ast/namespace.rs index 800dba52..9dba5b18 100644 --- a/src/ast/namespace.rs +++ b/src/ast/namespace.rs @@ -70,6 +70,7 @@ impl Deref for Namespace { type Target = StaticVec; #[inline(always)] + #[must_use] fn deref(&self) -> &Self::Target { &self.path } @@ -77,6 +78,7 @@ impl Deref for Namespace { impl DerefMut for Namespace { #[inline(always)] + #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.path } diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 68b63ecd..3b830100 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -424,6 +424,7 @@ impl Deref for StmtBlock { type Target = StmtBlockContainer; #[inline(always)] + #[must_use] fn deref(&self) -> &Self::Target { &self.block } @@ -431,6 +432,7 @@ impl Deref for StmtBlock { impl DerefMut for StmtBlock { #[inline(always)] + #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.block } @@ -438,6 +440,7 @@ impl DerefMut for StmtBlock { impl AsRef<[Stmt]> for StmtBlock { #[inline(always)] + #[must_use] fn as_ref(&self) -> &[Stmt] { &self.block } @@ -445,6 +448,7 @@ impl AsRef<[Stmt]> for StmtBlock { impl AsMut<[Stmt]> for StmtBlock { #[inline(always)] + #[must_use] fn as_mut(&mut self) -> &mut [Stmt] { &mut self.block } diff --git a/src/engine.rs b/src/engine.rs index 3520108f..72f95c6b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -240,12 +240,12 @@ impl Engine { source.map_or_else( || { if pos.is_none() { - println!("{}", s); + println!("{s}"); } else { - println!("{:?} | {}", pos, s); + println!("{pos:?} | {s}"); } }, - |source| println!("{} @ {:?} | {}", source, pos, s), + |source| println!("{source} @ {pos:?} | {s}"), ) }); } diff --git a/src/eval/target.rs b/src/eval/target.rs index 9c0804ab..3b47533d 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -399,6 +399,7 @@ impl Deref for Target<'_> { type Target = Dynamic; #[inline] + #[must_use] fn deref(&self) -> &Dynamic { match self { Self::RefMut(r) => r, @@ -416,6 +417,7 @@ impl Deref for Target<'_> { impl AsRef for Target<'_> { #[inline(always)] + #[must_use] fn as_ref(&self) -> &Dynamic { self } @@ -423,6 +425,7 @@ impl AsRef for Target<'_> { impl DerefMut for Target<'_> { #[inline] + #[must_use] fn deref_mut(&mut self) -> &mut Dynamic { match self { Self::RefMut(r) => r, @@ -440,6 +443,7 @@ impl DerefMut for Target<'_> { impl AsMut for Target<'_> { #[inline(always)] + #[must_use] fn as_mut(&mut self) -> &mut Dynamic { self } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index ee71ce13..729e772f 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -194,8 +194,8 @@ impl FileModuleResolver { /// Get a reference to the file module resolver's [scope][Scope]. /// /// The [scope][Scope] is used for compiling module scripts. - #[must_use] #[inline(always)] + #[must_use] pub const fn scope(&self) -> &Scope { &self.scope } @@ -211,8 +211,8 @@ impl FileModuleResolver { /// Get a mutable reference to the file module resolver's [scope][Scope]. /// /// The [scope][Scope] is used for compiling module scripts. - #[must_use] #[inline(always)] + #[must_use] pub fn scope_mut(&mut self) -> &mut Scope<'static> { &mut self.scope } diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 6823dd74..b310b302 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -252,6 +252,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { type Target = T; #[inline] + #[must_use] fn deref(&self) -> &Self::Target { match self.0 { DynamicWriteLockInner::Reference(ref reference) => *reference, @@ -263,6 +264,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { #[inline] + #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { match self.0 { DynamicWriteLockInner::Reference(ref mut reference) => *reference, diff --git a/src/types/error.rs b/src/types/error.rs index d426d5ed..822b1a39 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -23,6 +23,7 @@ use std::prelude::v1::*; /// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`]. #[derive(Debug)] #[non_exhaustive] +#[must_use] pub enum EvalAltResult { /// System error. Wrapped values are the error message and the internal error. #[cfg(not(feature = "sync"))] @@ -494,6 +495,7 @@ impl EvalAltResult { /// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards. #[cold] #[inline(never)] + #[must_use] pub fn take_position(&mut self) -> Position { let pos = self.position(); self.set_position(Position::NONE); diff --git a/src/types/immutable_string.rs b/src/types/immutable_string.rs index 6487efeb..507c33d6 100644 --- a/src/types/immutable_string.rs +++ b/src/types/immutable_string.rs @@ -53,6 +53,7 @@ impl Deref for ImmutableString { type Target = SmartString; #[inline(always)] + #[must_use] fn deref(&self) -> &Self::Target { &self.0 } @@ -60,6 +61,7 @@ impl Deref for ImmutableString { impl AsRef for ImmutableString { #[inline(always)] + #[must_use] fn as_ref(&self) -> &SmartString { &self.0 } @@ -67,6 +69,7 @@ impl AsRef for ImmutableString { impl AsRef for ImmutableString { #[inline(always)] + #[must_use] fn as_ref(&self) -> &str { &self.0 } @@ -74,6 +77,7 @@ impl AsRef for ImmutableString { impl Borrow for ImmutableString { #[inline(always)] + #[must_use] fn borrow(&self) -> &SmartString { &self.0 } @@ -81,6 +85,7 @@ impl Borrow for ImmutableString { impl Borrow for ImmutableString { #[inline(always)] + #[must_use] fn borrow(&self) -> &str { self.as_str() } @@ -623,7 +628,7 @@ impl ImmutableString { #[inline] #[must_use] pub fn into_owned(mut self) -> String { - self.make_mut(); // Make sure it is unique reference + let _ = self.make_mut(); // Make sure it is unique reference shared_take(self.0).into() // Should succeed } /// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references). @@ -631,6 +636,7 @@ impl ImmutableString { /// /// If there are other references to the same string, a cloned copy is used. #[inline(always)] + #[must_use] pub(crate) fn make_mut(&mut self) -> &mut SmartString { shared_make_mut(&mut self.0) } diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index 5cbdcb5d..e9b55ce3 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -13,6 +13,7 @@ use std::prelude::v1::*; /// Error encountered when tokenizing the script text. #[derive(Debug, Eq, PartialEq, Clone, Hash)] #[non_exhaustive] +#[must_use] pub enum LexError { /// An unexpected symbol is encountered when tokenizing the script text. UnexpectedInput(String), @@ -58,8 +59,8 @@ impl fmt::Display for LexError { impl LexError { /// Convert a [`LexError`] into a [`ParseError`]. - #[inline(always)] - #[must_use] + #[cold] + #[inline(never)] pub fn into_err(self, pos: Position) -> ParseError { ParseError(Box::new(self.into()), pos) } @@ -72,6 +73,7 @@ impl LexError { /// massive code changes to remove/add back enum variants in match statements. #[derive(Debug, Eq, PartialEq, Clone, Hash)] #[non_exhaustive] +#[must_use] pub enum ParseErrorType { /// The script ends prematurely. UnexpectedEOF, @@ -171,7 +173,8 @@ pub enum ParseErrorType { impl ParseErrorType { /// Make a [`ParseError`] using the current type and position. - #[inline(always)] + #[cold] + #[inline(never)] #[must_use] pub(crate) fn into_err(self, pos: Position) -> ParseError { ParseError(self.into(), pos) @@ -277,6 +280,7 @@ impl From for ParseErrorType { /// Error when parsing a script. #[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[must_use] pub struct ParseError( /// Parse error type. pub Box, diff --git a/tests/operations.rs b/tests/operations.rs index d6b7b69f..c4eb37ab 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -10,7 +10,7 @@ fn test_max_operations() -> Result<(), Box> { engine.on_progress(|count| { if count % 100 == 0 { - println!("{}", count); + println!("{count}"); } None }); @@ -68,7 +68,7 @@ fn test_max_operations_functions() -> Result<(), Box> { engine.on_progress(|count| { if count % 100 == 0 { - println!("{}", count); + println!("{count}"); } None }); @@ -124,7 +124,7 @@ fn test_max_operations_eval() -> Result<(), Box> { engine.on_progress(|count| { if count % 100 == 0 { - println!("{}", count); + println!("{count}"); } None }); diff --git a/tests/plugins.rs b/tests/plugins.rs index d5072eb9..5fe58fdc 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -42,7 +42,7 @@ mod test { #[rhai_fn(name = "no_effect", set = "no_effect", pure)] pub fn no_effect(array: &mut Array, value: INT) { // array is not modified - println!("Array = {:?}, Value = {}", array, value); + println!("Array = {array:?}, Value = {value}"); } } } diff --git a/tests/print.rs b/tests/print.rs index 820cce58..982b2ff2 100644 --- a/tests/print.rs +++ b/tests/print.rs @@ -66,7 +66,7 @@ fn test_print_debug() -> Result<(), Box> { ); for entry in logbook.read().unwrap().iter() { - println!("{}", entry); + println!("{entry}"); } Ok(()) diff --git a/tests/serde.rs b/tests/serde.rs index 5df11139..45c16b72 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -820,6 +820,6 @@ fn test_serde_blob() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] fn test_serde_json_borrowed_string() { let value = json!({ "a": "b" }); - println!("value: {:?}", value); + println!("value: {value:?}"); let _: Dynamic = serde_json::from_value(value).unwrap(); } diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 73701746..e249b6de 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -140,7 +140,7 @@ fn test_scope_eval() -> Result<(), Box> { // Second invocation using the same state let result = engine.eval_with_scope::(&mut scope, "x")?; - println!("result: {}", result); // should print 966 + println!("result: {result}"); // should print 966 // Variable y is changed in the script assert_eq!( From ce56c43bc070a3d6717b2752387d35d4d059fd7d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Sep 2022 16:20:36 +0800 Subject: [PATCH 004/116] Refactor serde impl. --- src/serde/de.rs | 99 +++++++++++++++------------ src/serde/deserialize.rs | 23 +++++++ src/serde/mod.rs | 5 +- src/serde/ser.rs | 109 +++++++++++++++++++---------- src/serde/serialize.rs | 1 + src/serde/str.rs | 143 --------------------------------------- 6 files changed, 157 insertions(+), 223 deletions(-) delete mode 100644 src/serde/str.rs diff --git a/src/serde/de.rs b/src/serde/de.rs index 76c31c11..dbf43605 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -8,12 +8,19 @@ use serde::{Deserialize, Deserializer}; use std::prelude::v1::*; use std::{any::type_name, fmt}; -/// Deserializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. -/// -/// The reference is necessary because the deserialized type may hold references -/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. -struct DynamicDeserializer<'a> { - value: &'a Dynamic, +/// Deserializer for [`Dynamic`][crate::Dynamic]. +pub struct DynamicDeserializer<'de> { + value: &'de Dynamic, +} + +impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic { + type Deserializer = DynamicDeserializer<'de>; + + #[inline(always)] + #[must_use] + fn into_deserializer(self) -> Self::Deserializer { + DynamicDeserializer { value: self } + } } impl<'de> DynamicDeserializer<'de> { @@ -21,15 +28,20 @@ impl<'de> DynamicDeserializer<'de> { /// /// The reference is necessary because the deserialized type may hold references /// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. + #[inline(always)] #[must_use] - pub const fn from_dynamic(value: &'de Dynamic) -> Self { + pub const fn new(value: &'de Dynamic) -> Self { Self { value } } /// Shortcut for a type conversion error. + #[cold] + #[inline(always)] fn type_error(&self) -> RhaiResultOf { self.type_error_str(type_name::()) } /// Shortcut for a type conversion error. + #[cold] + #[inline(never)] fn type_error_str(&self, error: &str) -> RhaiResultOf { Err(ERR::ErrorMismatchOutputType( error.into(), @@ -38,11 +50,8 @@ impl<'de> DynamicDeserializer<'de> { ) .into()) } - fn deserialize_int>( - &mut self, - v: crate::INT, - visitor: V, - ) -> RhaiResultOf { + #[inline(always)] + fn deserialize_int>(self, v: crate::INT, visitor: V) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return visitor.visit_i64(v); #[cfg(feature = "only_i32")] @@ -102,10 +111,12 @@ impl<'de> DynamicDeserializer<'de> { /// # } /// ``` pub fn from_dynamic<'de, T: Deserialize<'de>>(value: &'de Dynamic) -> RhaiResultOf { - T::deserialize(&mut DynamicDeserializer::from_dynamic(value)) + T::deserialize(DynamicDeserializer::new(value)) } impl Error for RhaiError { + #[cold] + #[inline(never)] fn custom(err: T) -> Self { LexError::ImproperSymbol(String::new(), err.to_string()) .into_err(Position::NONE) @@ -113,7 +124,7 @@ impl Error for RhaiError { } } -impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { +impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { type Error = RhaiError; fn deserialize_any>(self, visitor: V) -> RhaiResultOf { @@ -458,7 +469,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { if let (Some((key, value)), None) = (first, second) { visitor.visit_enum(EnumDeserializer { tag: key, - content: DynamicDeserializer::from_dynamic(value), + content: DynamicDeserializer::new(value), }) } else { self.type_error() @@ -470,10 +481,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } + #[inline(always)] fn deserialize_identifier>(self, visitor: V) -> RhaiResultOf { self.deserialize_str(visitor) } + #[inline(always)] fn deserialize_ignored_any>(self, visitor: V) -> RhaiResultOf { self.deserialize_any(visitor) } @@ -481,13 +494,14 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { /// `SeqAccess` implementation for arrays. #[cfg(not(feature = "no_index"))] -struct IterateDynamicArray<'a, ITER: Iterator> { +struct IterateDynamicArray<'de, ITER: Iterator> { /// Iterator for a stream of [`Dynamic`][crate::Dynamic] values. iter: ITER, } #[cfg(not(feature = "no_index"))] -impl<'a, ITER: Iterator> IterateDynamicArray<'a, ITER> { +impl<'de, ITER: Iterator> IterateDynamicArray<'de, ITER> { + #[inline(always)] #[must_use] pub const fn new(iter: ITER) -> Self { Self { iter } @@ -495,8 +509,8 @@ impl<'a, ITER: Iterator> IterateDynamicArray<'a, ITER> { } #[cfg(not(feature = "no_index"))] -impl<'a: 'de, 'de, ITER: Iterator> serde::de::SeqAccess<'de> - for IterateDynamicArray<'a, ITER> +impl<'de, ITER: Iterator> serde::de::SeqAccess<'de> + for IterateDynamicArray<'de, ITER> { type Error = RhaiError; @@ -506,17 +520,15 @@ impl<'a: 'de, 'de, ITER: Iterator> serde::de::SeqAccess<'de> ) -> RhaiResultOf> { // Deserialize each item coming out of the iterator. match self.iter.next() { + Some(item) => seed.deserialize(item.into_deserializer()).map(Some), None => Ok(None), - Some(item) => seed - .deserialize(&mut DynamicDeserializer::from_dynamic(item)) - .map(Some), } } } /// `MapAccess` implementation for maps. #[cfg(not(feature = "no_object"))] -struct IterateMap<'a, K: Iterator, V: Iterator> { +struct IterateMap<'de, K: Iterator, V: Iterator> { // Iterator for a stream of [`Dynamic`][crate::Dynamic] keys. keys: K, // Iterator for a stream of [`Dynamic`][crate::Dynamic] values. @@ -524,7 +536,8 @@ struct IterateMap<'a, K: Iterator, V: Iterator, V: Iterator> IterateMap<'a, K, V> { +impl<'de, K: Iterator, V: Iterator> IterateMap<'de, K, V> { + #[inline(always)] #[must_use] pub const fn new(keys: K, values: V) -> Self { Self { keys, values } @@ -532,8 +545,8 @@ impl<'a, K: Iterator, V: Iterator> IterateMa } #[cfg(not(feature = "no_object"))] -impl<'a: 'de, 'de, K: Iterator, V: Iterator> - serde::de::MapAccess<'de> for IterateMap<'a, K, V> +impl<'de, K: Iterator, V: Iterator> serde::de::MapAccess<'de> + for IterateMap<'de, K, V> { type Error = RhaiError; @@ -542,11 +555,9 @@ impl<'a: 'de, 'de, K: Iterator, V: Iterator> seed: S, ) -> RhaiResultOf> { // Deserialize each `Identifier` key coming out of the keys iterator. - match self.keys.next() { + match self.keys.next().map(<_>::into_deserializer) { + Some(d) => seed.deserialize(d).map(Some), None => Ok(None), - Some(item) => seed - .deserialize(&mut super::str::StringSliceDeserializer::from_str(item)) - .map(Some), } } @@ -555,20 +566,18 @@ impl<'a: 'de, 'de, K: Iterator, V: Iterator> seed: S, ) -> RhaiResultOf { // Deserialize each value item coming out of the iterator. - seed.deserialize(&mut DynamicDeserializer::from_dynamic( - self.values.next().unwrap(), - )) + seed.deserialize(self.values.next().unwrap().into_deserializer()) } } #[cfg(not(feature = "no_object"))] -struct EnumDeserializer<'t, 'de: 't> { - tag: &'t str, +struct EnumDeserializer<'de> { + tag: &'de str, content: DynamicDeserializer<'de>, } #[cfg(not(feature = "no_object"))] -impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { +impl<'de> serde::de::EnumAccess<'de> for EnumDeserializer<'de> { type Error = RhaiError; type Variant = Self; @@ -582,26 +591,30 @@ impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { } #[cfg(not(feature = "no_object"))] -impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> { +impl<'de> serde::de::VariantAccess<'de> for EnumDeserializer<'de> { type Error = RhaiError; - fn unit_variant(mut self) -> RhaiResultOf<()> { - Deserialize::deserialize(&mut self.content) + #[inline(always)] + fn unit_variant(self) -> RhaiResultOf<()> { + Deserialize::deserialize(self.content) } + #[inline(always)] fn newtype_variant_seed>( - mut self, + self, seed: T, ) -> RhaiResultOf { - seed.deserialize(&mut self.content) + seed.deserialize(self.content) } - fn tuple_variant>(mut self, len: usize, visitor: V) -> RhaiResultOf { + #[inline(always)] + fn tuple_variant>(self, len: usize, visitor: V) -> RhaiResultOf { self.content.deserialize_tuple(len, visitor) } + #[inline(always)] fn struct_variant>( - mut self, + self, fields: &'static [&'static str], visitor: V, ) -> RhaiResultOf { diff --git a/src/serde/deserialize.rs b/src/serde/deserialize.rs index 2f79ef6a..a41f8882 100644 --- a/src/serde/deserialize.rs +++ b/src/serde/deserialize.rs @@ -11,21 +11,28 @@ struct DynamicVisitor; impl<'d> Visitor<'d> for DynamicVisitor { type Value = Dynamic; + #[cold] + #[inline(never)] fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("any type that can be converted into a Dynamic") } + #[inline(always)] fn visit_bool(self, v: bool) -> Result { Ok(v.into()) } + #[inline(always)] fn visit_i8(self, v: i8) -> Result { Ok(INT::from(v).into()) } + #[inline(always)] fn visit_i16(self, v: i16) -> Result { Ok(INT::from(v).into()) } + #[inline(always)] fn visit_i32(self, v: i32) -> Result { Ok(INT::from(v).into()) } + #[inline] fn visit_i64(self, v: i64) -> Result { #[cfg(not(feature = "only_i32"))] { @@ -38,12 +45,15 @@ impl<'d> Visitor<'d> for DynamicVisitor { self.visit_i32(v as i32) } } + #[inline(always)] fn visit_u8(self, v: u8) -> Result { Ok(INT::from(v).into()) } + #[inline(always)] fn visit_u16(self, v: u16) -> Result { Ok(INT::from(v).into()) } + #[inline] fn visit_u32(self, v: u32) -> Result { #[cfg(not(feature = "only_i32"))] { @@ -56,6 +66,7 @@ impl<'d> Visitor<'d> for DynamicVisitor { self.visit_i32(v as i32) } } + #[inline] fn visit_u64(self, v: u64) -> Result { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as u64 { @@ -72,6 +83,7 @@ impl<'d> Visitor<'d> for DynamicVisitor { } #[cfg(not(feature = "no_float"))] + #[inline(always)] fn visit_f32(self, v: f32) -> Result { #[cfg(not(feature = "f32_float"))] return self.visit_f64(v as f64); @@ -79,6 +91,7 @@ impl<'d> Visitor<'d> for DynamicVisitor { return Ok(v.into()); } #[cfg(not(feature = "no_float"))] + #[inline(always)] fn visit_f64(self, v: f64) -> Result { #[cfg(not(feature = "f32_float"))] return Ok(v.into()); @@ -88,6 +101,7 @@ impl<'d> Visitor<'d> for DynamicVisitor { #[cfg(feature = "no_float")] #[cfg(feature = "decimal")] + #[inline] fn visit_f32(self, v: f32) -> Result { use rust_decimal::Decimal; use std::convert::TryFrom; @@ -98,6 +112,7 @@ impl<'d> Visitor<'d> for DynamicVisitor { } #[cfg(feature = "no_float")] #[cfg(feature = "decimal")] + #[inline] fn visit_f64(self, v: f64) -> Result { use rust_decimal::Decimal; use std::convert::TryFrom; @@ -107,23 +122,29 @@ impl<'d> Visitor<'d> for DynamicVisitor { .map_err(Error::custom) } + #[inline(always)] fn visit_char(self, v: char) -> Result { self.visit_string(v.to_string()) } + #[inline(always)] fn visit_str(self, v: &str) -> Result { Ok(v.into()) } + #[inline(always)] fn visit_borrowed_str(self, v: &str) -> Result { self.visit_str(v) } + #[inline(always)] fn visit_string(self, v: String) -> Result { Ok(v.into()) } + #[inline(always)] fn visit_unit(self) -> Result { Ok(Dynamic::UNIT) } + #[inline(always)] fn visit_newtype_struct>(self, de: D) -> Result { Deserialize::deserialize(de) } @@ -152,12 +173,14 @@ impl<'d> Visitor<'d> for DynamicVisitor { } impl<'d> Deserialize<'d> for Dynamic { + #[inline(always)] fn deserialize>(de: D) -> Result { de.deserialize_any(DynamicVisitor) } } impl<'d> Deserialize<'d> for ImmutableString { + #[inline] fn deserialize>(de: D) -> Result { let s: String = Deserialize::deserialize(de)?; Ok(s.into()) diff --git a/src/serde/mod.rs b/src/serde/mod.rs index 42b57e6e..4c78a939 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -6,7 +6,6 @@ mod deserialize; mod metadata; mod ser; mod serialize; -mod str; -pub use de::from_dynamic; -pub use ser::to_dynamic; +pub use de::{from_dynamic, DynamicDeserializer}; +pub use ser::{to_dynamic, DynamicSerializer}; diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 1cdc8e28..4104fd19 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,6 +1,6 @@ //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. -use crate::{Dynamic, Position, RhaiError, RhaiResult, RhaiResultOf, ERR}; +use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, }; @@ -9,10 +9,10 @@ use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; -/// Serializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. -struct DynamicSerializer { +/// Serializer for [`Dynamic`][crate::Dynamic]. +pub struct DynamicSerializer { /// Buffer to hold a temporary key. - _key: Dynamic, + _key: Identifier, /// Buffer to hold a temporary value. _value: Dynamic, } @@ -20,10 +20,10 @@ struct DynamicSerializer { impl DynamicSerializer { /// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value. #[must_use] - pub const fn new(_value: Dynamic) -> Self { + pub const fn new(value: Dynamic) -> Self { Self { - _key: Dynamic::UNIT, - _value, + _key: Identifier::new_const(), + _value: value, } } } @@ -105,10 +105,12 @@ impl Serializer for &mut DynamicSerializer { #[cfg(feature = "no_object")] type SerializeStructVariant = serde::ser::Impossible; + #[inline(always)] fn serialize_bool(self, v: bool) -> RhaiResultOf { Ok(v.into()) } + #[inline(always)] fn serialize_i8(self, v: i8) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); @@ -116,6 +118,7 @@ impl Serializer for &mut DynamicSerializer { return self.serialize_i32(i32::from(v)); } + #[inline(always)] fn serialize_i16(self, v: i16) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); @@ -123,6 +126,7 @@ impl Serializer for &mut DynamicSerializer { return self.serialize_i32(i32::from(v)); } + #[inline(always)] fn serialize_i32(self, v: i32) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); @@ -130,6 +134,7 @@ impl Serializer for &mut DynamicSerializer { return Ok(v.into()); } + #[inline] fn serialize_i64(self, v: i64) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] { @@ -143,6 +148,7 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline] fn serialize_i128(self, v: i128) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as i128 { @@ -158,6 +164,7 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline(always)] fn serialize_u8(self, v: u8) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); @@ -165,6 +172,7 @@ impl Serializer for &mut DynamicSerializer { return self.serialize_i32(i32::from(v)); } + #[inline(always)] fn serialize_u16(self, v: u16) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); @@ -172,6 +180,7 @@ impl Serializer for &mut DynamicSerializer { return self.serialize_i32(i32::from(v)); } + #[inline] fn serialize_u32(self, v: u32) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] { @@ -185,6 +194,7 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline] fn serialize_u64(self, v: u64) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as u64 { @@ -200,6 +210,7 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline] fn serialize_u128(self, v: u128) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as u128 { @@ -215,6 +226,7 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline] fn serialize_f32(self, v: f32) -> RhaiResultOf { #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] return Ok(Dynamic::from(v)); @@ -231,6 +243,7 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline] fn serialize_f64(self, v: f64) -> RhaiResultOf { #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] return Ok(Dynamic::from(v)); @@ -247,14 +260,17 @@ impl Serializer for &mut DynamicSerializer { } } + #[inline(always)] fn serialize_char(self, v: char) -> RhaiResultOf { Ok(v.into()) } + #[inline(always)] fn serialize_str(self, v: &str) -> RhaiResultOf { Ok(v.into()) } + #[inline] fn serialize_bytes(self, _v: &[u8]) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(Dynamic::from_blob(_v.to_vec())); @@ -262,28 +278,33 @@ impl Serializer for &mut DynamicSerializer { #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "BLOB's are not supported with 'no_index'".into(), + "BLOB's are not supported under 'no_index'".into(), Position::NONE, ) .into()); } + #[inline(always)] fn serialize_none(self) -> RhaiResultOf { Ok(Dynamic::UNIT) } + #[inline(always)] fn serialize_some(self, value: &T) -> RhaiResultOf { value.serialize(&mut *self) } + #[inline(always)] fn serialize_unit(self) -> RhaiResultOf { Ok(Dynamic::UNIT) } + #[inline(always)] fn serialize_unit_struct(self, _name: &'static str) -> RhaiResultOf { self.serialize_unit() } + #[inline(always)] fn serialize_unit_variant( self, _name: &'static str, @@ -293,6 +314,7 @@ impl Serializer for &mut DynamicSerializer { self.serialize_str(variant) } + #[inline(always)] fn serialize_newtype_struct( self, _name: &'static str, @@ -301,6 +323,7 @@ impl Serializer for &mut DynamicSerializer { value.serialize(&mut *self) } + #[inline] fn serialize_newtype_variant( self, _name: &'static str, @@ -316,28 +339,31 @@ impl Serializer for &mut DynamicSerializer { #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); } + #[inline] fn serialize_seq(self, _len: Option) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(DynamicSerializer::new(crate::Array::new().into())); #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "arrays are not supported with 'no_index'".into(), + "arrays are not supported under 'no_index'".into(), Position::NONE, ) .into()); } + #[inline(always)] fn serialize_tuple(self, len: usize) -> RhaiResultOf { self.serialize_seq(Some(len)) } + #[inline(always)] fn serialize_tuple_struct( self, _name: &'static str, @@ -346,6 +372,7 @@ impl Serializer for &mut DynamicSerializer { self.serialize_seq(Some(len)) } + #[inline] fn serialize_tuple_variant( self, _name: &'static str, @@ -362,24 +389,26 @@ impl Serializer for &mut DynamicSerializer { #[cfg(any(feature = "no_object", feature = "no_index"))] return Err(ERR::ErrorMismatchDataType( "".into(), - "tuples are not supported with 'no_index' or 'no_object'".into(), + "tuples are not supported under 'no_index' or 'no_object'".into(), Position::NONE, ) .into()); } + #[inline] fn serialize_map(self, _len: Option) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(DynamicSerializer::new(crate::Map::new().into())); #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); } + #[inline(always)] fn serialize_struct( self, _name: &'static str, @@ -388,6 +417,7 @@ impl Serializer for &mut DynamicSerializer { self.serialize_map(Some(len)) } + #[inline] fn serialize_struct_variant( self, _name: &'static str, @@ -403,7 +433,7 @@ impl Serializer for &mut DynamicSerializer { #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); @@ -425,20 +455,21 @@ impl SerializeSeq for DynamicSerializer { #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "arrays are not supported with 'no_index'".into(), + "arrays are not supported under 'no_index'".into(), Position::NONE, ) .into()); } // Close the sequence. + #[inline] fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "arrays are not supported with 'no_index'".into(), + "arrays are not supported under 'no_index'".into(), Position::NONE, ) .into()); @@ -460,19 +491,20 @@ impl SerializeTuple for DynamicSerializer { #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "tuples are not supported with 'no_index'".into(), + "tuples are not supported under 'no_index'".into(), Position::NONE, ) .into()); } + #[inline] fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "tuples are not supported with 'no_index'".into(), + "tuples are not supported under 'no_index'".into(), Position::NONE, ) .into()); @@ -494,19 +526,20 @@ impl SerializeTupleStruct for DynamicSerializer { #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "tuples are not supported with 'no_index'".into(), + "tuples are not supported under 'no_index'".into(), Position::NONE, ) .into()); } + #[inline] fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] return Err(ERR::ErrorMismatchDataType( "".into(), - "tuples are not supported with 'no_index'".into(), + "tuples are not supported under 'no_index'".into(), Position::NONE, ) .into()); @@ -520,13 +553,19 @@ impl SerializeMap for DynamicSerializer { fn serialize_key(&mut self, _key: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_object"))] { - self._key = _key.serialize(&mut *self)?; + let key = _key.serialize(&mut *self)?; + self._key = key + .into_immutable_string() + .map_err(|typ| { + ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) + })? + .into(); Ok(()) } #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); @@ -535,20 +574,16 @@ impl SerializeMap for DynamicSerializer { fn serialize_value(&mut self, _value: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_object"))] { - let key = std::mem::take(&mut self._key) - .into_immutable_string() - .map_err(|typ| { - ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) - })?; + let key = std::mem::take(&mut self._key); let value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); - map.insert(key.into(), value); + map.insert(key, value); Ok(()) } #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); @@ -573,19 +608,20 @@ impl SerializeMap for DynamicSerializer { #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); } + #[inline] fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(self._value); #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); @@ -611,19 +647,20 @@ impl SerializeStruct for DynamicSerializer { #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); } + #[inline] fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(self._value); #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), - "object maps are not supported with 'no_object'".into(), + "object maps are not supported under 'no_object'".into(), Position::NONE, ) .into()); @@ -632,7 +669,7 @@ impl SerializeStruct for DynamicSerializer { #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] -struct TupleVariantSerializer { +pub struct TupleVariantSerializer { variant: &'static str, array: crate::Array, } @@ -649,13 +686,14 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { Ok(()) } + #[inline] fn end(self) -> RhaiResultOf { make_variant(self.variant, self.array.into()) } } #[cfg(not(feature = "no_object"))] -struct StructVariantSerializer { +pub struct StructVariantSerializer { variant: &'static str, map: crate::Map, } @@ -665,6 +703,7 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer { type Ok = Dynamic; type Error = RhaiError; + #[inline] fn serialize_field( &mut self, key: &'static str, @@ -675,12 +714,14 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer { Ok(()) } + #[inline] fn end(self) -> RhaiResultOf { make_variant(self.variant, self.map.into()) } } #[cfg(not(feature = "no_object"))] +#[inline] fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult { let mut map = crate::Map::new(); map.insert(variant.into(), value); diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 579f0d1e..4aaa797e 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -83,6 +83,7 @@ impl Serialize for Dynamic { } impl Serialize for ImmutableString { + #[inline(always)] fn serialize(&self, ser: S) -> Result { ser.serialize_str(self.as_str()) } diff --git a/src/serde/str.rs b/src/serde/str.rs deleted file mode 100644 index df747a42..00000000 --- a/src/serde/str.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`]. - -use crate::{Position, RhaiError, RhaiResultOf, ERR}; -use serde::de::{Deserializer, Visitor}; -use std::any::type_name; -#[cfg(feature = "no_std")] -use std::prelude::v1::*; - -/// Deserializer for `ImmutableString`. -pub struct StringSliceDeserializer<'a> { - value: &'a str, -} - -impl<'a> StringSliceDeserializer<'a> { - /// Create an `ImmutableStringDeserializer` from an `&str` reference. - #[must_use] - pub const fn from_str(value: &'a str) -> Self { - Self { value } - } - /// Shortcut for a type conversion error. - fn type_error(&self) -> RhaiResultOf { - Err( - ERR::ErrorMismatchOutputType(type_name::().into(), "string".into(), Position::NONE) - .into(), - ) - } -} - -impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> { - type Error = RhaiError; - - fn deserialize_any>(self, v: V) -> RhaiResultOf { - self.deserialize_str(v) - } - fn deserialize_bool>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_i8>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_i16>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_i32>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_i64>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_u8>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_u16>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_u32>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_u64>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_f32>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_f64>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_char>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_str>(self, v: V) -> RhaiResultOf { - // Only allow deserialization into a string. - v.visit_borrowed_str(self.value) - } - fn deserialize_string>(self, visitor: V) -> RhaiResultOf { - self.deserialize_str(visitor) - } - fn deserialize_bytes>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_byte_buf>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_option>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_unit>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_unit_struct>( - self, - _name: &'static str, - v: V, - ) -> RhaiResultOf { - self.deserialize_unit(v) - } - fn deserialize_newtype_struct>( - self, - _name: &'static str, - v: V, - ) -> RhaiResultOf { - v.visit_newtype_struct(self) - } - fn deserialize_seq>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_tuple>(self, _len: usize, v: V) -> RhaiResultOf { - self.deserialize_seq(v) - } - fn deserialize_tuple_struct>( - self, - _name: &'static str, - _len: usize, - v: V, - ) -> RhaiResultOf { - self.deserialize_seq(v) - } - fn deserialize_map>(self, _: V) -> RhaiResultOf { - self.type_error() - } - fn deserialize_struct>( - self, - _name: &'static str, - _fields: &'static [&'static str], - v: V, - ) -> RhaiResultOf { - self.deserialize_map(v) - } - fn deserialize_enum>( - self, - _name: &'static str, - _variants: &'static [&'static str], - _: V, - ) -> RhaiResultOf { - self.type_error() - } - fn deserialize_identifier>(self, v: V) -> RhaiResultOf { - self.deserialize_str(v) - } - fn deserialize_ignored_any>(self, v: V) -> RhaiResultOf { - self.deserialize_any(v) - } -} From ece522ce2f57514fb78ed580a178c05c6efad727 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Sep 2022 18:22:48 +0800 Subject: [PATCH 005/116] Refactor fast operators. --- src/eval/expr.rs | 96 +++++++---------------------------- src/eval/stmt.rs | 26 +++++++--- src/func/callable_function.rs | 2 +- src/optimizer.rs | 4 +- 4 files changed, 39 insertions(+), 89 deletions(-) diff --git a/src/eval/expr.rs b/src/eval/expr.rs index af762e20..ff3abaff 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -3,17 +3,9 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, FnCallExpr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; -use crate::eval::FnResolutionCacheEntry; -use crate::func::{ - calc_fn_params_hash, combine_hashes, gen_fn_call_signature, get_builtin_binary_op_fn, - CallableFunction, -}; +use crate::func::get_builtin_binary_op_fn; use crate::types::dynamic::AccessMode; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; -#[cfg(feature = "no_std")] -use hashbrown::hash_map::Entry; -#[cfg(not(feature = "no_std"))] -use std::collections::hash_map::Entry; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -231,83 +223,31 @@ impl Engine { } = expr; // Short-circuit native binary operator call if under Fast Operators mode - if expr.is_native_operator && self.fast_operators() && (args.len() == 1 || args.len() == 2) - { + if expr.is_native_operator && self.fast_operators() && args.len() == 2 { let mut lhs = self .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? .0 .flatten(); - let mut rhs = if args.len() == 2 { - self.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? - .0 - .flatten() - } else { - Dynamic::UNIT - }; + let mut rhs = self + .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? + .0 + .flatten(); - let mut operands = [&mut lhs, &mut rhs]; - let operands = if args.len() == 2 { - &mut operands[..] - } else { - &mut operands[0..1] - }; + let operands = &mut [&mut lhs, &mut rhs]; - let hash = calc_fn_params_hash(operands.iter().map(|a| a.type_id())); - let hash = combine_hashes(hashes.native, hash); + if let Some(func) = get_builtin_binary_op_fn(name, operands[0], operands[1]) { + // Built-in found + let context = (self, name, None, &*global, lib, pos, level + 1).into(); + let result = func(context, operands); + return self.check_return_value(result, pos); + } - let cache = caches.fn_resolution_cache_mut(); - let local_entry: CallableFunction; - - let func = match cache.map.entry(hash) { - Entry::Vacant(entry) => { - let func = if args.len() == 2 { - get_builtin_binary_op_fn(name, operands[0], operands[1]) - } else { - None - }; - - if let Some(f) = func { - if cache.filter.is_absent_and_set(hash) { - // Do not cache "one-hit wonders" - local_entry = CallableFunction::from_fn_builtin(f); - &local_entry - } else { - // Cache repeated calls - &entry - .insert(Some(FnResolutionCacheEntry { - func: CallableFunction::from_fn_builtin(f), - source: None, - })) - .as_ref() - .unwrap() - .func - } - } else { - let result = self.exec_fn_call( - None, global, caches, lib, name, *hashes, operands, false, false, pos, - level, - ); - return result.map(|(v, ..)| v); - } - } - Entry::Occupied(entry) => { - if let Some(entry) = entry.into_mut() { - &entry.func - } else { - let sig = gen_fn_call_signature(self, name, operands); - return Err(ERR::ErrorFunctionNotFound(sig, pos).into()); - } - } - }; - - let context = (self, name, None, &*global, lib, pos, level).into(); - let result = if func.is_plugin_fn() { - func.get_plugin_fn().unwrap().call(context, operands) - } else { - func.get_native_fn().unwrap()(context, operands) - }; - return self.check_return_value(result, pos); + return self + .exec_fn_call( + None, global, caches, lib, name, *hashes, operands, false, false, pos, level, + ) + .map(|(v, ..)| v); } #[cfg(not(feature = "no_module"))] diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 60587b4f..bfb79908 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -5,7 +5,7 @@ use crate::api::events::VarDefInfo; use crate::ast::{ ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock, }; -use crate::func::get_hasher; +use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; use crate::{ Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, @@ -145,6 +145,19 @@ impl Engine { let args = &mut [&mut *lock_guard, &mut new_val]; let level = level + 1; + if self.fast_operators() { + if let Some(func) = get_builtin_op_assignment_fn(op_assign, args[0], args[1]) { + // Built-in found + let context = (self, op_assign, None, &*global, lib, op_pos, level).into(); + let result = func(context, args).map(|_| ()); + + #[cfg(not(feature = "unchecked"))] + self.check_data_size(args[0], root.1)?; + + return result; + } + } + match self.call_native_fn( global, caches, lib, op_assign, hash, args, true, true, op_pos, level, ) { @@ -155,16 +168,13 @@ impl Engine { Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` - let (value, ..) = self + *args[0] = self .call_native_fn( global, caches, lib, op, hash_op, args, true, false, op_pos, level, ) - .map_err(|err| err.fill_position(op_info.pos))?; - - #[cfg(not(feature = "unchecked"))] - self.check_data_size(&value, root.1)?; - - *args[0] = value.flatten(); + .map_err(|err| err.fill_position(op_info.pos))? + .0 + .flatten(); } Err(err) => return Err(err), } diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index c08eb845..babf1ebf 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -197,7 +197,7 @@ impl CallableFunction { Self::Script(..) => None, } } - /// Create a new [`CallableFunction::Method`] from `FnBuiltin`. + /// Create a new [`CallableFunction::Method`] from a [built-in function][`FnBuiltin`]. #[inline(always)] #[must_use] pub fn from_fn_builtin(func: FnBuiltin) -> Self { diff --git a/src/optimizer.rs b/src/optimizer.rs index b9a4dd97..216e4f3c 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1229,12 +1229,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = state.lib.iter().copied().any(|m| m.get_script_fn(&x.name, x.args.len()).is_some()); + let has_script_fn = state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some(); #[cfg(feature = "no_function")] let has_script_fn = false; if !has_script_fn { - let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::>(); + let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::>>().unwrap(); let result = match x.name.as_str() { KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), From bf02d040e2c6971291498f9fdbf2606d7c8f1ecb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Sep 2022 23:03:18 +0800 Subject: [PATCH 006/116] Use tokens to speed up function name lookup. --- src/ast/expr.rs | 8 +- src/ast/stmt.rs | 17 +- src/eval/chaining.rs | 4 +- src/eval/expr.rs | 20 +- src/eval/stmt.rs | 18 +- src/func/builtin.rs | 413 +++++++++++++++++------------------- src/func/call.rs | 31 +-- src/optimizer.rs | 4 +- src/packages/blob_basic.rs | 15 ++ src/packages/iter_basic.rs | 11 + src/packages/map_basic.rs | 14 ++ src/packages/string_more.rs | 31 +++ src/parser.rs | 65 +++--- src/tests.rs | 10 +- src/tokenizer.rs | 105 ++++----- 15 files changed, 417 insertions(+), 349 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 389ac137..e29745a7 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -193,7 +193,7 @@ pub struct FnCallExpr { /// Does this function call capture the parent scope? pub capture_parent_scope: bool, /// Is this function call a native operator? - pub is_native_operator: bool, + pub operator_token: Option, /// [Position] of the function name. pub pos: Position, } @@ -208,8 +208,8 @@ impl fmt::Debug for FnCallExpr { if self.capture_parent_scope { ff.field("capture_parent_scope", &self.capture_parent_scope); } - if self.is_native_operator { - ff.field("is_native_operator", &self.is_native_operator); + if let Some(ref token) = self.operator_token { + ff.field("operator_token", token); } ff.field("hash", &self.hashes) .field("name", &self.name) @@ -673,7 +673,7 @@ impl Expr { hashes: calc_fn_hash(None, f.fn_name(), 1).into(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, - is_native_operator: false, + operator_token: None, pos, } .into(), diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 3b830100..3bbb0177 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -19,16 +19,16 @@ use std::{ /// Exported under the `internals` feature only. /// /// This type may hold a straight assignment (i.e. not an op-assignment). -#[derive(Clone, Copy, Eq, PartialEq, Hash)] +#[derive(Clone, PartialEq, Hash)] pub struct OpAssignment { /// Hash of the op-assignment call. pub hash_op_assign: u64, /// Hash of the underlying operator call (for fallback). pub hash_op: u64, /// Op-assignment operator. - pub op_assign: &'static str, + pub op_assign: Token, /// Underlying operator. - pub op: &'static str, + pub op: Token, /// [Position] of the op-assignment operator. pub pos: Position, } @@ -41,8 +41,8 @@ impl OpAssignment { Self { hash_op_assign: 0, hash_op: 0, - op_assign: "=", - op: "=", + op_assign: Token::Equals, + op: Token::Equals, pos, } } @@ -71,12 +71,11 @@ impl OpAssignment { pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self { let op_raw = op .get_base_op_from_assignment() - .expect("op-assignment operator") - .literal_syntax(); + .expect("op-assignment operator"); Self { hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2), - hash_op: calc_fn_hash(None, op_raw, 2), - op_assign: op.literal_syntax(), + hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2), + op_assign: op.clone(), op: op_raw, pos, } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 0e6869f9..abdd0573 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -50,7 +50,7 @@ impl Engine { idx_values: &mut FnArgsVec, chain_type: ChainType, level: usize, - new_val: Option<(Dynamic, OpAssignment)>, + new_val: Option<(Dynamic, &OpAssignment)>, ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); @@ -558,7 +558,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, level: usize, - new_val: Option<(Dynamic, OpAssignment)>, + new_val: Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { let chain_type = ChainType::from(expr); let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr { diff --git a/src/eval/expr.rs b/src/eval/expr.rs index ff3abaff..cdbeb95e 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -219,11 +219,15 @@ impl Engine { level: usize, ) -> RhaiResult { let FnCallExpr { - name, hashes, args, .. + name, + hashes, + args, + operator_token, + .. } = expr; // Short-circuit native binary operator call if under Fast Operators mode - if expr.is_native_operator && self.fast_operators() && args.len() == 2 { + if operator_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? .0 @@ -236,7 +240,9 @@ impl Engine { let operands = &mut [&mut lhs, &mut rhs]; - if let Some(func) = get_builtin_binary_op_fn(name, operands[0], operands[1]) { + if let Some(func) = + get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1]) + { // Built-in found let context = (self, name, None, &*global, lib, pos, level + 1).into(); let result = func(context, operands); @@ -278,7 +284,7 @@ impl Engine { args, *hashes, expr.capture_parent_scope, - expr.is_native_operator, + expr.operator_token.as_ref(), pos, level, ) @@ -384,9 +390,9 @@ impl Engine { op_info.pos = expr.start_position(); - if let Err(err) = self - .eval_op_assignment(global, caches, lib, op_info, target, root, item, level) - { + if let Err(err) = self.eval_op_assignment( + global, caches, lib, &op_info, target, root, item, level, + ) { result = Err(err); break; } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index bfb79908..58f7ce47 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -117,7 +117,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], - op_info: OpAssignment, + op_info: &OpAssignment, target: &mut Target, root: (&str, Position), new_val: Dynamic, @@ -141,14 +141,15 @@ impl Engine { let mut lock_guard = target.write_lock::().unwrap(); - let hash = hash_op_assign; + let hash = *hash_op_assign; let args = &mut [&mut *lock_guard, &mut new_val]; let level = level + 1; if self.fast_operators() { if let Some(func) = get_builtin_op_assignment_fn(op_assign, args[0], args[1]) { // Built-in found - let context = (self, op_assign, None, &*global, lib, op_pos, level).into(); + let op = op_assign.literal_syntax(); + let context = (self, op, None, &*global, lib, *op_pos, level).into(); let result = func(context, args).map(|_| ()); #[cfg(not(feature = "unchecked"))] @@ -158,8 +159,11 @@ impl Engine { } } + let op_assign = op_assign.literal_syntax(); + let op = op.literal_syntax(); + match self.call_native_fn( - global, caches, lib, op_assign, hash, args, true, true, op_pos, level, + global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { Ok(_) => { #[cfg(not(feature = "unchecked"))] @@ -170,7 +174,7 @@ impl Engine { // Expand to `var = var op rhs` *args[0] = self .call_native_fn( - global, caches, lib, op, hash_op, args, true, false, op_pos, level, + global, caches, lib, op, *hash_op, args, true, false, *op_pos, level, ) .map_err(|err| err.fill_position(op_info.pos))? .0 @@ -279,7 +283,7 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; self.eval_op_assignment( - global, caches, lib, *op_info, lhs_ptr, root, rhs_val, level, + global, caches, lib, op_info, lhs_ptr, root, rhs_val, level, ) .map(|_| Dynamic::UNIT) } else { @@ -303,7 +307,7 @@ impl Engine { rhs_val }; - let _new_val = Some((rhs_val, *op_info)); + let _new_val = Some((rhs_val, op_info)); // Must be either `var[index] op= val` or `var.prop op= val` match lhs { diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 4ad42130..9052a5fe 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -2,7 +2,7 @@ use super::call::FnCallArgs; use super::native::FnBuiltin; -use crate::engine::OP_CONTAINS; +use crate::tokenizer::Token; use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT}; use std::any::TypeId; #[cfg(feature = "no_std")] @@ -67,7 +67,7 @@ fn is_numeric(type_id: TypeId) -> bool { /// /// The return function will be registered as a _method_, so the first parameter cannot be consumed. #[must_use] -pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option { +pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option { let type1 = x.type_id(); let type2 = y.type_id(); @@ -131,46 +131,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(INT => add(as_int, as_int))), - "-" => return Some(impl_op!(INT => subtract(as_int, as_int))), - "*" => return Some(impl_op!(INT => multiply(as_int, as_int))), - "/" => return Some(impl_op!(INT => divide(as_int, as_int))), - "%" => return Some(impl_op!(INT => modulo(as_int, as_int))), - "**" => return Some(impl_op!(INT => power(as_int, as_int))), - ">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))), - "<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))), + Token::Plus => return Some(impl_op!(INT => add(as_int, as_int))), + Token::Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), + Token::Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), + Token::Divide => return Some(impl_op!(INT => divide(as_int, as_int))), + Token::Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), + Token::PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), + Token::RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))), + Token::LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - "+" => return Some(impl_op!(INT => as_int + as_int)), - "-" => return Some(impl_op!(INT => as_int - as_int)), - "*" => return Some(impl_op!(INT => as_int * as_int)), - "/" => return Some(impl_op!(INT => as_int / as_int)), - "%" => return Some(impl_op!(INT => as_int % as_int)), - "**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - ">>" => return Some(impl_op!(INT => as_int >> as_int)), - "<<" => return Some(impl_op!(INT => as_int << as_int)), + Token::Plus => return Some(impl_op!(INT => as_int + as_int)), + Token::Minus => return Some(impl_op!(INT => as_int - as_int)), + Token::Multiply => return Some(impl_op!(INT => as_int * as_int)), + Token::Divide => return Some(impl_op!(INT => as_int / as_int)), + Token::Modulo => return Some(impl_op!(INT => as_int % as_int)), + Token::PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + Token::RightShift => return Some(impl_op!(INT => as_int >> as_int)), + Token::LeftShift => return Some(impl_op!(INT => as_int << as_int)), _ => (), } return match op { - "==" => Some(impl_op!(INT => as_int == as_int)), - "!=" => Some(impl_op!(INT => as_int != as_int)), - ">" => Some(impl_op!(INT => as_int > as_int)), - ">=" => Some(impl_op!(INT => as_int >= as_int)), - "<" => Some(impl_op!(INT => as_int < as_int)), - "<=" => Some(impl_op!(INT => as_int <= as_int)), - "&" => Some(impl_op!(INT => as_int & as_int)), - "|" => Some(impl_op!(INT => as_int | as_int)), - "^" => Some(impl_op!(INT => as_int ^ as_int)), - ".." => Some(|_, args| { + Token::EqualsTo => Some(impl_op!(INT => as_int == as_int)), + Token::NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), + Token::GreaterThan => Some(impl_op!(INT => as_int > as_int)), + Token::GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), + Token::LessThan => Some(impl_op!(INT => as_int < as_int)), + Token::LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), + Token::Ampersand => Some(impl_op!(INT => as_int & as_int)), + Token::Pipe => Some(impl_op!(INT => as_int | as_int)), + Token::XOr => Some(impl_op!(INT => as_int ^ as_int)), + Token::ExclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..y).into()) }), - "..=" => Some(|_, args| { + Token::InclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..=y).into()) @@ -181,47 +181,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option() { return match op { - "==" => Some(impl_op!(bool => as_bool == as_bool)), - "!=" => Some(impl_op!(bool => as_bool != as_bool)), - ">" => Some(impl_op!(bool => as_bool > as_bool)), - ">=" => Some(impl_op!(bool => as_bool >= as_bool)), - "<" => Some(impl_op!(bool => as_bool < as_bool)), - "<=" => Some(impl_op!(bool => as_bool <= as_bool)), - "&" => Some(impl_op!(bool => as_bool & as_bool)), - "|" => Some(impl_op!(bool => as_bool | as_bool)), - "^" => Some(impl_op!(bool => as_bool ^ as_bool)), + Token::EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), + Token::NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), + Token::GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), + Token::GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), + Token::LessThan => Some(impl_op!(bool => as_bool < as_bool)), + Token::LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), + Token::Ampersand => Some(impl_op!(bool => as_bool & as_bool)), + Token::Pipe => Some(impl_op!(bool => as_bool | as_bool)), + Token::XOr => Some(impl_op!(bool => as_bool ^ as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "+" => Some(impl_op!(ImmutableString + ImmutableString)), - "-" => Some(impl_op!(ImmutableString - ImmutableString)), - "==" => Some(impl_op!(ImmutableString == ImmutableString)), - "!=" => Some(impl_op!(ImmutableString != ImmutableString)), - ">" => Some(impl_op!(ImmutableString > ImmutableString)), - ">=" => Some(impl_op!(ImmutableString >= ImmutableString)), - "<" => Some(impl_op!(ImmutableString < ImmutableString)), - "<=" => Some(impl_op!(ImmutableString <= ImmutableString)), - OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))), + Token::Plus => Some(impl_op!(ImmutableString + ImmutableString)), + Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), + Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), + Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), + Token::GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), + Token::GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), + Token::LessThan => Some(impl_op!(ImmutableString < ImmutableString)), + Token::LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "+" => Some(|_, args| { + Token::Plus => Some(|_, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok(format!("{x}{y}").into()) }), - "==" => Some(impl_op!(char => as_char == as_char)), - "!=" => Some(impl_op!(char => as_char != as_char)), - ">" => Some(impl_op!(char => as_char > as_char)), - ">=" => Some(impl_op!(char => as_char >= as_char)), - "<" => Some(impl_op!(char => as_char < as_char)), - "<=" => Some(impl_op!(char => as_char <= as_char)), + Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), + Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), + Token::GreaterThan => Some(impl_op!(char => as_char > as_char)), + Token::GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), + Token::LessThan => Some(impl_op!(char => as_char < as_char)), + Token::LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), _ => None, }; } @@ -231,7 +230,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(|_, args| { + Token::Plus => Some(|_, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -245,16 +244,20 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(Blob == Blob)), - "!=" => Some(impl_op!(Blob != Blob)), + Token::EqualsTo => Some(impl_op!(Blob == Blob)), + Token::NotEqualsTo => Some(impl_op!(Blob != Blob)), _ => None, }; } if type1 == TypeId::of::<()>() { return match op { - "==" => Some(|_, _| Ok(Dynamic::TRUE)), - "!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + Token::EqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Token::NotEqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, }; } @@ -265,18 +268,18 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - "+" => Some(impl_op!(FLOAT => $xx + $yy)), - "-" => Some(impl_op!(FLOAT => $xx - $yy)), - "*" => Some(impl_op!(FLOAT => $xx * $yy)), - "/" => Some(impl_op!(FLOAT => $xx / $yy)), - "%" => Some(impl_op!(FLOAT => $xx % $yy)), - "**" => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), - "==" => Some(impl_op!(FLOAT => $xx == $yy)), - "!=" => Some(impl_op!(FLOAT => $xx != $yy)), - ">" => Some(impl_op!(FLOAT => $xx > $yy)), - ">=" => Some(impl_op!(FLOAT => $xx >= $yy)), - "<" => Some(impl_op!(FLOAT => $xx < $yy)), - "<=" => Some(impl_op!(FLOAT => $xx <= $yy)), + Token::Plus => Some(impl_op!(FLOAT => $xx + $yy)), + Token::Minus => Some(impl_op!(FLOAT => $xx - $yy)), + Token::Multiply => Some(impl_op!(FLOAT => $xx * $yy)), + Token::Divide => Some(impl_op!(FLOAT => $xx / $yy)), + Token::Modulo => Some(impl_op!(FLOAT => $xx % $yy)), + Token::PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), + Token::EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), + Token::NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), + Token::GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), + Token::GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), + Token::LessThan => Some(impl_op!(FLOAT => $xx < $yy)), + Token::LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), _ => None, }; } @@ -299,12 +302,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(from Decimal => add($xx, $yy))), - "-" => return Some(impl_op!(from Decimal => subtract($xx, $yy))), - "*" => return Some(impl_op!(from Decimal => multiply($xx, $yy))), - "/" => return Some(impl_op!(from Decimal => divide($xx, $yy))), - "%" => return Some(impl_op!(from Decimal => modulo($xx, $yy))), - "**" => return Some(impl_op!(from Decimal => power($xx, $yy))), + Token::Plus => return Some(impl_op!(from Decimal => add($xx, $yy))), + Token::Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), + Token::Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), + Token::Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), + Token::Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), + Token::PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), _ => () } @@ -313,22 +316,22 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(from Decimal => $xx + $yy)), - "-" => return Some(impl_op!(from Decimal => $xx - $yy)), - "*" => return Some(impl_op!(from Decimal => $xx * $yy)), - "/" => return Some(impl_op!(from Decimal => $xx / $yy)), - "%" => return Some(impl_op!(from Decimal => $xx % $yy)), - "**" => return Some(impl_op!(from Decimal => $xx.powd($yy))), + Token::Plus => return Some(impl_op!(from Decimal => $xx + $yy)), + Token::Minus => return Some(impl_op!(from Decimal => $xx - $yy)), + Token::Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), + Token::Divide => return Some(impl_op!(from Decimal => $xx / $yy)), + Token::Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), + Token::PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), _ => () } return match op { - "==" => Some(impl_op!(from Decimal => $xx == $yy)), - "!=" => Some(impl_op!(from Decimal => $xx != $yy)), - ">" => Some(impl_op!(from Decimal => $xx > $yy)), - ">=" => Some(impl_op!(from Decimal => $xx >= $yy)), - "<" => Some(impl_op!(from Decimal => $xx < $yy)), - "<=" => Some(impl_op!(from Decimal => $xx <= $yy)), + Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), + Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), + Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), + Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), + Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)), + Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), _ => None }; } @@ -354,17 +357,17 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(|_, args| { + Token::Plus => Some(|_, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); Ok(format!("{x}{y}").into()) }), - "==" => Some(impl_op!(get_s1s2(==))), - "!=" => Some(impl_op!(get_s1s2(!=))), - ">" => Some(impl_op!(get_s1s2(>))), - ">=" => Some(impl_op!(get_s1s2(>=))), - "<" => Some(impl_op!(get_s1s2(<))), - "<=" => Some(impl_op!(get_s1s2(<=))), + Token::EqualsTo => Some(impl_op!(get_s1s2(==))), + Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + Token::GreaterThan => Some(impl_op!(get_s1s2(>))), + Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + Token::LessThan => Some(impl_op!(get_s1s2(<))), + Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } @@ -380,45 +383,48 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(|_, args| { + Token::Plus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok((x + y).into()) }), - "-" => Some(|_, args| { + Token::Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok((x - y).into()) }), - "==" => Some(impl_op!(get_s1s2(==))), - "!=" => Some(impl_op!(get_s1s2(!=))), - ">" => Some(impl_op!(get_s1s2(>))), - ">=" => Some(impl_op!(get_s1s2(>=))), - "<" => Some(impl_op!(get_s1s2(<))), - "<=" => Some(impl_op!(get_s1s2(<=))), - OP_CONTAINS => Some(|_, args| { - let s = &*args[0].read_lock::().expect(BUILTIN); - let c = args[1].as_char().expect(BUILTIN); - Ok(s.contains(c).into()) - }), + Token::EqualsTo => Some(impl_op!(get_s1s2(==))), + Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + Token::GreaterThan => Some(impl_op!(get_s1s2(>))), + Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + Token::LessThan => Some(impl_op!(get_s1s2(<))), + Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } // () op string if (type1, type2) == (TypeId::of::<()>(), TypeId::of::()) { return match op { - "+" => Some(|_, args| Ok(args[1].clone())), - "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), - "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + Token::Plus => Some(|_, args| Ok(args[1].clone())), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), _ => None, }; } // string op () if (type1, type2) == (TypeId::of::(), TypeId::of::<()>()) { return match op { - "+" => Some(|_, args| Ok(args[0].clone())), - "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), - "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + Token::Plus => Some(|_, args| Ok(args[0].clone())), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), _ => None, }; } @@ -428,19 +434,9 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option() { use crate::Blob; - if type2 == TypeId::of::() { - return match op { - OP_CONTAINS => Some(|_, args| { - let blob = &*args[0].read_lock::().expect(BUILTIN); - let x = (args[1].as_int().expect("`INT`") & 0x0000_00ff) as u8; - Ok((!blob.is_empty() && blob.contains(&x)).into()) - }), - _ => None, - }; - } if type2 == TypeId::of::() { return match op { - "+" => Some(|_, args| { + Token::Plus => Some(|_, args| { let mut buf = [0_u8; 4]; let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf); @@ -452,17 +448,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option(), TypeId::of::()) { - use crate::Map; - - return match op { - OP_CONTAINS => Some(impl_op!(Map.contains_key(ImmutableString.as_str()))), - _ => None, - }; - } - // Non-compatible ranges if (type1, type2) == ( @@ -476,48 +461,28 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option None, - }; - } if type1 == type2 { return match op { - "==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)), - "!=" => Some(impl_op!(ExclusiveRange != ExclusiveRange)), + Token::EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), + Token::NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), _ => None, }; } } if type1 == TypeId::of::() { - if type2 == TypeId::of::() { - return match op { - OP_CONTAINS => Some(|_, args| { - let range = &*args[0].read_lock::().expect(BUILTIN); - let x = args[1].as_int().expect("`INT`"); - Ok(range.contains(&x).into()) - }), - _ => None, - }; - } if type1 == type2 { return match op { - "==" => Some(impl_op!(InclusiveRange == InclusiveRange)), - "!=" => Some(impl_op!(InclusiveRange != InclusiveRange)), + Token::EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), + Token::NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), _ => None, }; } @@ -531,8 +496,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, } } else { @@ -544,8 +513,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, }; } @@ -558,7 +531,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Option { +pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option { let type1 = x.type_id(); let type2 = y.type_id(); @@ -610,49 +583,51 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio #[cfg(not(feature = "unchecked"))] match op { - "+=" => return Some(impl_op!(INT => add(as_int, as_int))), - "-=" => return Some(impl_op!(INT => subtract(as_int, as_int))), - "*=" => return Some(impl_op!(INT => multiply(as_int, as_int))), - "/=" => return Some(impl_op!(INT => divide(as_int, as_int))), - "%=" => return Some(impl_op!(INT => modulo(as_int, as_int))), - "**=" => return Some(impl_op!(INT => power(as_int, as_int))), - ">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))), - "<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))), + Token::PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), + Token::MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), + Token::MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), + Token::DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), + Token::ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), + Token::PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), + Token::RightShiftAssign => { + return Some(impl_op!(INT => shift_right(as_int, as_int))) + } + Token::LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - "+=" => return Some(impl_op!(INT += as_int)), - "-=" => return Some(impl_op!(INT -= as_int)), - "*=" => return Some(impl_op!(INT *= as_int)), - "/=" => return Some(impl_op!(INT /= as_int)), - "%=" => return Some(impl_op!(INT %= as_int)), - "**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - ">>=" => return Some(impl_op!(INT >>= as_int)), - "<<=" => return Some(impl_op!(INT <<= as_int)), + Token::PlusAssign => return Some(impl_op!(INT += as_int)), + Token::MinusAssign => return Some(impl_op!(INT -= as_int)), + Token::MultiplyAssign => return Some(impl_op!(INT *= as_int)), + Token::DivideAssign => return Some(impl_op!(INT /= as_int)), + Token::ModuloAssign => return Some(impl_op!(INT %= as_int)), + Token::PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + Token::RightShiftAssign => return Some(impl_op!(INT >>= as_int)), + Token::LeftShiftAssign => return Some(impl_op!(INT <<= as_int)), _ => (), } return match op { - "&=" => Some(impl_op!(INT &= as_int)), - "|=" => Some(impl_op!(INT |= as_int)), - "^=" => Some(impl_op!(INT ^= as_int)), + Token::AndAssign => Some(impl_op!(INT &= as_int)), + Token::OrAssign => Some(impl_op!(INT |= as_int)), + Token::XOrAssign => Some(impl_op!(INT ^= as_int)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "&=" => Some(impl_op!(bool = x && as_bool)), - "|=" => Some(impl_op!(bool = x || as_bool)), + Token::AndAssign => Some(impl_op!(bool = x && as_bool)), + Token::OrAssign => Some(impl_op!(bool = x || as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let y = args[1].as_char().expect(BUILTIN); let x = &mut *args[0].write_lock::().expect(BUILTIN); Ok((*x = format!("{x}{y}").into()).into()) @@ -663,13 +638,13 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio if type1 == TypeId::of::() { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); Ok((*x += y).into()) }), - "-=" => Some(|_, args| { + Token::MinusAssign => Some(|_, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); @@ -684,7 +659,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio use crate::Blob; return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let blob2 = std::mem::take(args[1]).cast::(); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) @@ -699,12 +674,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio ($x:ident, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - "+=" => Some(impl_op!($x += $yy)), - "-=" => Some(impl_op!($x -= $yy)), - "*=" => Some(impl_op!($x *= $yy)), - "/=" => Some(impl_op!($x /= $yy)), - "%=" => Some(impl_op!($x %= $yy)), - "**=" => Some(impl_op!($x => $xx.powf($yy as $x))), + Token::PlusAssign => Some(impl_op!($x += $yy)), + Token::MinusAssign => Some(impl_op!($x -= $yy)), + Token::MultiplyAssign => Some(impl_op!($x *= $yy)), + Token::DivideAssign => Some(impl_op!($x /= $yy)), + Token::ModuloAssign => Some(impl_op!($x %= $yy)), + Token::PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), _ => None, }; } @@ -726,12 +701,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio #[cfg(not(feature = "unchecked"))] return match op { - "+=" => Some(impl_op!(from $x => add($xx, $yy))), - "-=" => Some(impl_op!(from $x => subtract($xx, $yy))), - "*=" => Some(impl_op!(from $x => multiply($xx, $yy))), - "/=" => Some(impl_op!(from $x => divide($xx, $yy))), - "%=" => Some(impl_op!(from $x => modulo($xx, $yy))), - "**=" => Some(impl_op!(from $x => power($xx, $yy))), + Token::PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), + Token::MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), + Token::MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), + Token::DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), + Token::ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), + Token::PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), _ => None, }; @@ -740,12 +715,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio #[cfg(feature = "unchecked")] return match op { - "+=" => Some(impl_op!(from $x += $yy)), - "-=" => Some(impl_op!(from $x -= $yy)), - "*=" => Some(impl_op!(from $x *= $yy)), - "/=" => Some(impl_op!(from $x /= $yy)), - "%=" => Some(impl_op!(from $x %= $yy)), - "**=" => Some(impl_op!(from $x => $xx.powd($yy))), + Token::PlusAssign => Some(impl_op!(from $x += $yy)), + Token::MinusAssign => Some(impl_op!(from $x -= $yy)), + Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)), + Token::DivideAssign => Some(impl_op!(from $x /= $yy)), + Token::ModuloAssign => Some(impl_op!(from $x %= $yy)), + Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), _ => None, }; } @@ -761,15 +736,15 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(impl_op!(ImmutableString += as_char as char)), - "-=" => Some(impl_op!(ImmutableString -= as_char as char)), + Token::PlusAssign => Some(impl_op!(ImmutableString += as_char as char)), + Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; } // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let mut ch = args[0].as_char().expect(BUILTIN).to_string(); ch.push_str( args[1] @@ -793,7 +768,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio if type2 == TypeId::of::() { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let array2 = std::mem::take(args[1]).cast::(); let array1 = &mut *args[0].write_lock::().expect(BUILTIN); Ok(append(array1, array2).into()) @@ -802,7 +777,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio }; } return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let x = std::mem::take(args[1]); let array = &mut *args[0].write_lock::().expect(BUILTIN); Ok(push(array, x).into()) @@ -818,7 +793,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let x = args[1].as_int().expect("`INT`"); let blob = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) @@ -830,7 +805,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let x = args[1].as_char().expect("`char`"); let blob = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) @@ -842,7 +817,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let s = std::mem::take(args[1]).cast::(); let blob = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) diff --git a/src/func/call.rs b/src/func/call.rs index e91d63a8..ea6349fe 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -9,6 +9,7 @@ use crate::engine::{ KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; +use crate::tokenizer::Token; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, @@ -191,7 +192,7 @@ impl Engine { hash_base: u64, args: Option<&mut FnCallArgs>, allow_dynamic: bool, - is_op_assignment: bool, + op_assignment_token: Option<&Token>, ) -> Option<&'s FnResolutionCacheEntry> { if hash_base == 0 { return None; @@ -279,22 +280,23 @@ impl Engine { // Try to find a built-in version let builtin = args.and_then(|args| { - if is_op_assignment { + if let Some(op_assign) = op_assignment_token { let (first_arg, rest_args) = args.split_first().unwrap(); - get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0]).map( - |f| FnResolutionCacheEntry { + get_builtin_op_assignment_fn(op_assign, *first_arg, rest_args[0]) + .map(|f| FnResolutionCacheEntry { func: CallableFunction::from_fn_builtin(f), source: None, - }, - ) - } else { - get_builtin_binary_op_fn(fn_name, args[0], args[1]).map(|f| { + }) + } else if let Some(ref operator) = Token::lookup_from_syntax(fn_name) { + get_builtin_binary_op_fn(operator, args[0], args[1]).map(|f| { FnResolutionCacheEntry { func: CallableFunction::from_fn_builtin(f), source: None, } }) + } else { + None } }); @@ -360,6 +362,11 @@ impl Engine { self.inc_operations(&mut global.num_operations, pos)?; let parent_source = global.source.clone(); + let op_assign = if is_op_assign { + Token::lookup_from_syntax(name) + } else { + None + }; // Check if function access already in the cache let local_entry = &mut None; @@ -373,7 +380,7 @@ impl Engine { hash, Some(args), true, - is_op_assign, + op_assign.as_ref(), ); if func.is_some() { @@ -653,7 +660,7 @@ impl Engine { hashes.script, None, false, - false, + None, ) .cloned() { @@ -993,7 +1000,7 @@ impl Engine { args_expr: &[Expr], hashes: FnCallHashes, capture_scope: bool, - is_operator: bool, + operator_token: Option<&Token>, pos: Position, level: usize, ) -> RhaiResult { @@ -1006,7 +1013,7 @@ impl Engine { let redirected; // Handle call() - Redirect function call match name { - _ if is_operator => (), + _ if operator_token.is_some() => (), // Handle call() KEYWORD_FN_PTR_CALL if total_args >= 1 => { diff --git a/src/optimizer.rs b/src/optimizer.rs index 216e4f3c..6ab19fcb 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1181,8 +1181,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { - if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1]) + _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { + if let Some(result) = get_builtin_binary_op_fn(x.operator_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] let lib = state.lib; diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index a3bbd532..4b6639c2 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -146,6 +146,21 @@ pub mod blob_functions { pub fn is_empty(blob: &mut Blob) -> bool { blob.len() == 0 } + /// Return `true` if the BLOB contains a specified byte value. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.contains('h')); // prints true + /// + /// print(text.contains('x')); // prints false + /// ``` + #[rhai_fn(name = "contains")] + pub fn contains(blob: &mut Blob, value: INT) -> bool { + blob.contains(&((value & 0x0000_00ff) as u8)) + } /// Get the byte value at the `index` position in the BLOB. /// /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element). diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 6ec293cc..b9c849fe 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -668,6 +668,12 @@ mod range_functions { pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool { range.is_empty() } + /// Return `true` if the range contains a specified value. + #[rhai_fn(name = "contains")] + pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool { + range.contains(&value) + } + /// Return the start of the inclusive range. #[rhai_fn(get = "start", name = "start", pure)] pub fn start_inclusive(range: &mut InclusiveRange) -> INT { @@ -695,4 +701,9 @@ mod range_functions { pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool { range.is_empty() } + /// Return `true` if the range contains a specified value. + #[rhai_fn(name = "contains")] + pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool { + range.contains(&value) + } } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 09ffdce6..955b8395 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -30,6 +30,20 @@ mod map_functions { pub fn is_empty(map: &mut Map) -> bool { map.len() == 0 } + /// Returns `true` if the object map contains a specified property. + /// + /// # Example + /// + /// ```rhai + /// let m = #{a: 1, b: 2, c: 3}; + /// + /// print(m.contains("b")); // prints true + /// + /// print(m.contains("x")); // prints false + /// ``` + pub fn contains(map: &mut Map, property: &str) -> bool { + map.contains_key(property) + } /// Get the value of the `property` in the object map and return a copy. /// /// If `property` does not exist in the object map, `()` is returned. diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index da53a127..93e8edf4 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -506,6 +506,37 @@ mod string_functions { *character = to_lower_char(*character); } + /// Return `true` if the string contains a specified string. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.contains("hello")); // prints true + /// + /// print(text.contains("hey")); // prints false + /// ``` + pub fn contains(string: &str, match_string: &str) -> bool { + string.contains(match_string) + } + + /// Return `true` if the string contains a specified character. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.contains('h')); // prints true + /// + /// print(text.contains('x')); // prints false + /// ``` + #[rhai_fn(name = "contains")] + pub fn contains_char(string: &str, character: char) -> bool { + string.contains(character).into() + } + /// Return `true` if the string starts with a specified string. /// /// # Example diff --git a/src/parser.rs b/src/parser.rs index 621a1b47..9941fc53 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -472,7 +472,7 @@ fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> { match input.next().expect(NEVER_ENDS) { // Variable name - (Token::Identifier(s), pos) => Ok((s, pos)), + (Token::Identifier(s), pos) => Ok((*s, pos)), // Reserved keyword (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { Err(PERR::Reserved(s.to_string()).into_err(pos)) @@ -492,7 +492,7 @@ fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> // Symbol (token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)), // Reserved symbol - (Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((s, pos)), + (Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((*s, pos)), // Bad symbol (Token::LexError(err), pos) => Err(err.into_err(pos)), // Not a symbol @@ -616,7 +616,7 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - is_native_operator: false, + operator_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -684,7 +684,7 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - is_native_operator: false, + operator_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -1000,10 +1000,10 @@ impl Engine { let (name, pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s) | Token::StringConstant(s), pos) => { - if map.iter().any(|(p, ..)| **p == s) { + if map.iter().any(|(p, ..)| **p == *s) { return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); } - (s, pos) + (*s, pos) } (Token::InterpolatedString(..), pos) => { return Err(PERR::PropertyExpected.into_err(pos)) @@ -1342,7 +1342,7 @@ impl Engine { Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::StringConstant(s) => { - Expr::StringConstant(state.get_interned_string(s), settings.pos) + Expr::StringConstant(state.get_interned_string(*s), settings.pos) } Token::True => Expr::BoolConstant(true, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos), @@ -1356,7 +1356,7 @@ impl Engine { } #[cfg(feature = "decimal")] Token::DecimalConstant(x) => { - let x = (*x).into(); + let x = (**x).into(); input.next(); Expr::DynamicConstant(Box::new(x), settings.pos) } @@ -1479,7 +1479,7 @@ impl Engine { match input.next().expect(NEVER_ENDS) { (Token::InterpolatedString(s), ..) if s.is_empty() => (), (Token::InterpolatedString(s), pos) => { - segments.push(Expr::StringConstant(s.into(), pos)) + segments.push(Expr::StringConstant(state.get_interned_string(*s), pos)) } token => { unreachable!("Token::InterpolatedString expected but gets {:?}", token) @@ -1502,14 +1502,16 @@ impl Engine { match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { if !s.is_empty() { - segments.push(Expr::StringConstant(s.into(), pos)); + segments + .push(Expr::StringConstant(state.get_interned_string(*s), pos)); } // End the interpolated string if it is terminated by a back-tick. break; } (Token::InterpolatedString(s), pos) => { if !s.is_empty() { - segments.push(Expr::StringConstant(s.into(), pos)); + segments + .push(Expr::StringConstant(state.get_interned_string(*s), pos)); } } (Token::LexError(err), pos) @@ -1574,7 +1576,7 @@ impl Engine { state.allow_capture = true; } Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), + (None, ns, 0, state.get_interned_string(*s)).into(), None, settings.pos, ) @@ -1587,7 +1589,7 @@ impl Engine { // Once the identifier consumed we must enable next variables capturing state.allow_capture = true; } - let name = state.get_interned_string(s); + let name = state.get_interned_string(*s); Expr::Variable((None, ns, 0, name).into(), None, settings.pos) } // Normal variable access @@ -1612,7 +1614,7 @@ impl Engine { None } }); - let name = state.get_interned_string(s); + let name = state.get_interned_string(*s); Expr::Variable((index, ns, 0, name).into(), short_index, settings.pos) } } @@ -1634,7 +1636,7 @@ impl Engine { // Function call is allowed to have reserved keyword Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => { Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), + (None, ns, 0, state.get_interned_string(*s)).into(), None, settings.pos, ) @@ -1642,7 +1644,7 @@ impl Engine { // Access to `this` as a variable is OK within a function scope #[cfg(not(feature = "no_function"))] _ if &*s == KEYWORD_THIS && settings.in_fn_scope => Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), + (None, ns, 0, state.get_interned_string(*s)).into(), None, settings.pos, ), @@ -1888,7 +1890,7 @@ impl Engine { // -expr Token::Minus | Token::UnaryMinus => { let token = token.clone(); - let pos = eat_token(input, token); + let pos = eat_token(input, token.clone()); match self.parse_unary(input, state, lib, settings.level_up())? { // Negative integer @@ -1918,7 +1920,7 @@ impl Engine { hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), args, pos, - is_native_operator: true, + operator_token: Some(token), ..Default::default() } .into_fn_call_expr(pos)) @@ -1928,7 +1930,7 @@ impl Engine { // +expr Token::Plus | Token::UnaryPlus => { let token = token.clone(); - let pos = eat_token(input, token); + let pos = eat_token(input, token.clone()); match self.parse_unary(input, state, lib, settings.level_up())? { expr @ Expr::IntegerConstant(..) => Ok(expr), @@ -1946,7 +1948,7 @@ impl Engine { hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), args, pos, - is_native_operator: true, + operator_token: Some(token), ..Default::default() } .into_fn_call_expr(pos)) @@ -1955,7 +1957,9 @@ impl Engine { } // !expr Token::Bang => { + let token = token.clone(); let pos = eat_token(input, Token::Bang); + let mut args = StaticVec::new_const(); args.push(self.parse_unary(input, state, lib, settings.level_up())?); args.shrink_to_fit(); @@ -1965,7 +1969,7 @@ impl Engine { hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), args, pos, - is_native_operator: true, + operator_token: Some(token), ..Default::default() } .into_fn_call_expr(pos)) @@ -2283,7 +2287,7 @@ impl Engine { #[cfg(not(feature = "no_custom_syntax"))] Token::Custom(c) => self .custom_keywords - .get(c) + .get(&**c) .copied() .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?, Token::Reserved(c) if !is_valid_identifier(c.chars()) => { @@ -2308,7 +2312,7 @@ impl Engine { #[cfg(not(feature = "no_custom_syntax"))] Token::Custom(c) => self .custom_keywords - .get(c) + .get(&**c) .copied() .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?, Token::Reserved(c) if !is_valid_identifier(c.chars()) => { @@ -2335,12 +2339,17 @@ impl Engine { let op = op_token.syntax(); let hash = calc_fn_hash(None, &op, 2); + let operator_token = if is_valid_function_name(&op) { + None + } else { + Some(op_token.clone()) + }; let op_base = FnCallExpr { name: state.get_interned_string(op.as_ref()), hashes: FnCallHashes::from_native(hash), pos, - is_native_operator: !is_valid_function_name(&op), + operator_token, ..Default::default() }; @@ -2578,7 +2587,7 @@ impl Engine { }, CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { - let s = state.get_interned_string(s); + let s = state.get_interned_string(*s); inputs.push(Expr::StringConstant(s.clone(), pos)); segments.push(s); tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING)); @@ -3228,7 +3237,7 @@ impl Engine { match input.next().expect(NEVER_ENDS).0 { Token::Comment(comment) => { - comments.push(comment); + comments.push(*comment); match input.peek().expect(NEVER_ENDS) { (Token::Fn | Token::Private, ..) => break, @@ -3562,7 +3571,7 @@ impl Engine { return Err(PERR::FnDuplicatedParam(name.to_string(), s.to_string()) .into_err(pos)); } - let s = state.get_interned_string(s); + let s = state.get_interned_string(*s); state.stack.push(s.clone(), ()); params.push((s, pos)); } @@ -3700,7 +3709,7 @@ impl Engine { PERR::FnDuplicatedParam(String::new(), s.to_string()).into_err(pos) ); } - let s = state.get_interned_string(s); + let s = state.get_interned_string(*s); state.stack.push(s.clone(), ()); params_list.push(s); } diff --git a/src/tests.rs b/src/tests.rs index 1d897871..c91a1e1e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -22,15 +22,7 @@ fn check_struct_sizes() { ); assert_eq!( size_of::(), - if IS_32_BIT { - if cfg!(feature = "decimal") { - 24 - } else { - 16 - } - } else { - 32 - } + if IS_32_BIT { 8 } else { 16 } ); assert_eq!(size_of::(), if PACKED { 12 } else { 16 }); assert_eq!(size_of::>(), if PACKED { 12 } else { 16 }); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6cc7eb57..e3d615e8 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -381,15 +381,15 @@ pub enum Token { /// /// Requires the `decimal` feature. #[cfg(feature = "decimal")] - DecimalConstant(rust_decimal::Decimal), + DecimalConstant(Box), /// An identifier. - Identifier(Identifier), + Identifier(Box), /// A character constant. CharConstant(char), /// A string constant. - StringConstant(SmartString), + StringConstant(Box), /// An interpolated string. - InterpolatedString(SmartString), + InterpolatedString(Box), /// `{` LeftBrace, /// `}` @@ -570,14 +570,14 @@ pub enum Token { /// A lexer error. LexError(Box), /// A comment block. - Comment(SmartString), + Comment(Box), /// A reserved symbol. - Reserved(SmartString), + Reserved(Box), /// A custom keyword. /// /// Not available under `no_custom_syntax`. #[cfg(not(feature = "no_custom_syntax"))] - Custom(SmartString), + Custom(Box), /// End of the input stream. EOF, } @@ -898,21 +898,23 @@ impl Token { // List of reserved operators "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" - | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => Reserved(syntax.into()), + | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => { + Reserved(Box::new(syntax.into())) + } // List of reserved keywords "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" - | "async" | "await" | "yield" => Reserved(syntax.into()), + | "async" | "await" | "yield" => Reserved(Box::new(syntax.into())), KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { - Reserved(syntax.into()) + Reserved(Box::new(syntax.into())) } #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN => Reserved(syntax.into()), + crate::engine::KEYWORD_IS_DEF_FN => Reserved(Box::new(syntax.into())), _ => return None, }) @@ -1097,8 +1099,8 @@ impl Token { pub(crate) fn into_function_name_for_override(self) -> Result { match self { #[cfg(not(feature = "no_custom_syntax"))] - Self::Custom(s) if is_valid_function_name(&s) => Ok(s), - Self::Identifier(s) if is_valid_function_name(&s) => Ok(s), + Self::Custom(s) if is_valid_function_name(&s) => Ok(*s), + Self::Identifier(s) if is_valid_function_name(&s) => Ok(*s), _ => Err(self), } } @@ -1510,7 +1512,7 @@ fn get_next_token_inner( let return_comment = return_comment || is_doc_comment(comment.as_ref().expect("`Some`")); if return_comment { - return Some((Token::Comment(comment.expect("`Some`")), start_pos)); + return Some((Token::Comment(comment.expect("`Some`").into()), start_pos)); } if state.comment_level > 0 { // Reached EOF without ending comment block @@ -1524,9 +1526,9 @@ fn get_next_token_inner( |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(result, interpolated, start_pos)| { if interpolated { - Some((Token::InterpolatedString(result), start_pos)) + Some((Token::InterpolatedString(result.into()), start_pos)) } else { - Some((Token::StringConstant(result), start_pos)) + Some((Token::StringConstant(result.into()), start_pos)) } }, ); @@ -1676,13 +1678,16 @@ fn get_next_token_inner( // Then try decimal #[cfg(feature = "decimal")] let num = num.or_else(|_| { - rust_decimal::Decimal::from_str(&result).map(Token::DecimalConstant) + rust_decimal::Decimal::from_str(&result) + .map(Box::new) + .map(Token::DecimalConstant) }); // Then try decimal in scientific notation #[cfg(feature = "decimal")] let num = num.or_else(|_| { rust_decimal::Decimal::from_scientific(&result) + .map(Box::new) .map(Token::DecimalConstant) }); @@ -1709,7 +1714,7 @@ fn get_next_token_inner( return parse_string_literal(stream, state, pos, c, false, true, false) .map_or_else( |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), - |(result, ..)| Some((Token::StringConstant(result), start_pos)), + |(result, ..)| Some((Token::StringConstant(result.into()), start_pos)), ); } // ` - string literal @@ -1737,9 +1742,9 @@ fn get_next_token_inner( |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(result, interpolated, ..)| { if interpolated { - Some((Token::InterpolatedString(result), start_pos)) + Some((Token::InterpolatedString(result.into()), start_pos)) } else { - Some((Token::StringConstant(result), start_pos)) + Some((Token::StringConstant(result.into()), start_pos)) } }, ); @@ -1786,7 +1791,7 @@ fn get_next_token_inner( // Parentheses ('(', '*') => { eat_next(stream, pos); - return Some((Token::Reserved("(*".into()), start_pos)); + return Some((Token::Reserved(Box::new("(*".into())), start_pos)); } ('(', ..) => return Some((Token::LeftParen, start_pos)), (')', ..) => return Some((Token::RightParen, start_pos)), @@ -1802,7 +1807,7 @@ fn get_next_token_inner( return Some((Token::MapStart, start_pos)); } // Shebang - ('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), + ('#', '!') => return Some((Token::Reserved(Box::new("#!".into())), start_pos)), ('#', ' ') => { eat_next(stream, pos); @@ -1812,10 +1817,10 @@ fn get_next_token_inner( } else { "#" }; - return Some((Token::Reserved(token.into()), start_pos)); + return Some((Token::Reserved(Box::new(token.into())), start_pos)); } - ('#', ..) => return Some((Token::Reserved("#".into()), start_pos)), + ('#', ..) => return Some((Token::Reserved(Box::new("#".into())), start_pos)), // Operators ('+', '=') => { @@ -1824,7 +1829,7 @@ fn get_next_token_inner( } ('+', '+') => { eat_next(stream, pos); - return Some((Token::Reserved("++".into()), start_pos)); + return Some((Token::Reserved(Box::new("++".into())), start_pos)); } ('+', ..) if !state.next_token_cannot_be_unary => { return Some((Token::UnaryPlus, start_pos)) @@ -1839,11 +1844,11 @@ fn get_next_token_inner( } ('-', '>') => { eat_next(stream, pos); - return Some((Token::Reserved("->".into()), start_pos)); + return Some((Token::Reserved(Box::new("->".into())), start_pos)); } ('-', '-') => { eat_next(stream, pos); - return Some((Token::Reserved("--".into()), start_pos)); + return Some((Token::Reserved(Box::new("--".into())), start_pos)); } ('-', ..) if !state.next_token_cannot_be_unary => { return Some((Token::UnaryMinus, start_pos)) @@ -1852,7 +1857,7 @@ fn get_next_token_inner( ('*', ')') => { eat_next(stream, pos); - return Some((Token::Reserved("*)".into()), start_pos)); + return Some((Token::Reserved(Box::new("*)".into())), start_pos)); } ('*', '=') => { eat_next(stream, pos); @@ -1925,7 +1930,7 @@ fn get_next_token_inner( .borrow_mut() .global_comments .push(comment), - _ => return Some((Token::Comment(comment), start_pos)), + _ => return Some((Token::Comment(comment.into()), start_pos)), } } } @@ -1953,7 +1958,7 @@ fn get_next_token_inner( scan_block_comment(stream, state.comment_level, pos, comment.as_mut()); if let Some(comment) = comment { - return Some((Token::Comment(comment), start_pos)); + return Some((Token::Comment(comment.into()), start_pos)); } } @@ -1972,7 +1977,7 @@ fn get_next_token_inner( match stream.peek_next() { Some('.') => { eat_next(stream, pos); - Token::Reserved("...".into()) + Token::Reserved(Box::new("...".into())) } Some('=') => { eat_next(stream, pos); @@ -1990,7 +1995,7 @@ fn get_next_token_inner( if stream.peek_next() == Some('=') { eat_next(stream, pos); - return Some((Token::Reserved("===".into()), start_pos)); + return Some((Token::Reserved(Box::new("===".into())), start_pos)); } return Some((Token::EqualsTo, start_pos)); @@ -2007,18 +2012,18 @@ fn get_next_token_inner( if stream.peek_next() == Some('<') { eat_next(stream, pos); - return Some((Token::Reserved("::<".into()), start_pos)); + return Some((Token::Reserved(Box::new("::<".into())), start_pos)); } return Some((Token::DoubleColon, start_pos)); } (':', '=') => { eat_next(stream, pos); - return Some((Token::Reserved(":=".into()), start_pos)); + return Some((Token::Reserved(Box::new(":=".into())), start_pos)); } (':', ';') => { eat_next(stream, pos); - return Some((Token::Reserved(":;".into()), start_pos)); + return Some((Token::Reserved(Box::new(":;".into())), start_pos)); } (':', ..) => return Some((Token::Colon, start_pos)), @@ -2028,7 +2033,7 @@ fn get_next_token_inner( } ('<', '-') => { eat_next(stream, pos); - return Some((Token::Reserved("<-".into()), start_pos)); + return Some((Token::Reserved(Box::new("<-".into())), start_pos)); } ('<', '<') => { eat_next(stream, pos); @@ -2045,7 +2050,7 @@ fn get_next_token_inner( } ('<', '|') => { eat_next(stream, pos); - return Some((Token::Reserved("<|".into()), start_pos)); + return Some((Token::Reserved(Box::new("<|".into())), start_pos)); } ('<', ..) => return Some((Token::LessThan, start_pos)), @@ -2073,14 +2078,14 @@ fn get_next_token_inner( if stream.peek_next() == Some('=') { eat_next(stream, pos); - return Some((Token::Reserved("!==".into()), start_pos)); + return Some((Token::Reserved(Box::new("!==".into())), start_pos)); } return Some((Token::NotEqualsTo, start_pos)); } ('!', '.') => { eat_next(stream, pos); - return Some((Token::Reserved("!.".into()), start_pos)); + return Some((Token::Reserved(Box::new("!.".into())), start_pos)); } ('!', ..) => return Some((Token::Bang, start_pos)), @@ -2094,7 +2099,7 @@ fn get_next_token_inner( } ('|', '>') => { eat_next(stream, pos); - return Some((Token::Reserved("|>".into()), start_pos)); + return Some((Token::Reserved(Box::new("|>".into())), start_pos)); } ('|', ..) => return Some((Token::Pipe, start_pos)), @@ -2114,7 +2119,7 @@ fn get_next_token_inner( } ('^', ..) => return Some((Token::XOr, start_pos)), - ('~', ..) => return Some((Token::Reserved("~".into()), start_pos)), + ('~', ..) => return Some((Token::Reserved(Box::new("~".into())), start_pos)), ('%', '=') => { eat_next(stream, pos); @@ -2122,9 +2127,9 @@ fn get_next_token_inner( } ('%', ..) => return Some((Token::Modulo, start_pos)), - ('@', ..) => return Some((Token::Reserved("@".into()), start_pos)), + ('@', ..) => return Some((Token::Reserved(Box::new("@".into())), start_pos)), - ('$', ..) => return Some((Token::Reserved("$".into()), start_pos)), + ('$', ..) => return Some((Token::Reserved(Box::new("$".into())), start_pos)), ('?', '.') => { eat_next(stream, pos); @@ -2132,7 +2137,7 @@ fn get_next_token_inner( #[cfg(not(feature = "no_object"))] Token::Elvis, #[cfg(feature = "no_object")] - Token::Reserved("?.".into()), + Token::Reserved(Box::new("?.".into())), start_pos, )); } @@ -2146,11 +2151,11 @@ fn get_next_token_inner( #[cfg(not(feature = "no_index"))] Token::QuestionBracket, #[cfg(feature = "no_index")] - Token::Reserved("?[".into()), + Token::Reserved(Box::new("?[".into())), start_pos, )); } - ('?', ..) => return Some((Token::Reserved("?".into()), start_pos)), + ('?', ..) => return Some((Token::Reserved(Box::new("?".into())), start_pos)), (ch, ..) if ch.is_whitespace() => (), @@ -2201,7 +2206,7 @@ fn get_identifier( ); } - (Token::Identifier(identifier), start_pos) + (Token::Identifier(identifier.into()), start_pos) } /// Is a keyword allowed as a function? @@ -2382,7 +2387,7 @@ impl<'a> Iterator for TokenIterator<'a> { } // Reserved keyword/symbol Some((Token::Reserved(s), pos)) => (match - (&*s, + (s.as_str(), #[cfg(not(feature = "no_custom_syntax"))] (!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)), #[cfg(feature = "no_custom_syntax")] @@ -2438,7 +2443,7 @@ impl<'a> Iterator for TokenIterator<'a> { Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => { if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) { // Disabled standard keyword/symbol - (Token::Custom(token.literal_syntax().into()), pos) + (Token::Custom(Box::new(token.literal_syntax().into())), pos) } else { // Active standard keyword - should never be a custom keyword! unreachable!("{:?} is an active keyword", token) @@ -2446,7 +2451,7 @@ impl<'a> Iterator for TokenIterator<'a> { } // Disabled symbol Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => { - (Token::Reserved(token.literal_syntax().into()), pos) + (Token::Reserved(Box::new(token.literal_syntax().into())), pos) } // Normal symbol Some(r) => r, From e9a270f6baace6cf54087114671dcee97d4de829 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Sep 2022 23:07:47 +0800 Subject: [PATCH 007/116] Fix feature builds. --- src/tokenizer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e3d615e8..7cea143e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -874,9 +874,9 @@ impl Token { "**=" => PowerOfAssign, #[cfg(feature = "no_object")] - "?." => Reserved(syntax.into()), + "?." => Reserved(Box::new(syntax.into())), #[cfg(feature = "no_index")] - "?[" => Reserved(syntax.into()), + "?[" => Reserved(Box::new(syntax.into())), #[cfg(not(feature = "no_function"))] "fn" => Fn, @@ -884,7 +884,7 @@ impl Token { "private" => Private, #[cfg(feature = "no_function")] - "fn" | "private" => Reserved(syntax.into()), + "fn" | "private" => Reserved(Box::new(syntax.into())), #[cfg(not(feature = "no_module"))] "import" => Import, @@ -894,7 +894,7 @@ impl Token { "as" => As, #[cfg(feature = "no_module")] - "import" | "export" | "as" => Reserved(syntax.into()), + "import" | "export" | "as" => Reserved(Box::new(syntax.into())), // List of reserved operators "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" From 7d87a59315bb433bc6eeaf774180a29fdaacdf16 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Sep 2022 23:45:51 +0800 Subject: [PATCH 008/116] Fix doc test. --- src/api/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/events.rs b/src/api/events.rs index 01463d2b..42511be3 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -172,7 +172,7 @@ impl Engine { /// engine.on_parse_token(|token, _, _| { /// match token { /// // Convert all integer literals to strings - /// Token::IntegerConstant(n) => Token::StringConstant(n.to_string().into()), + /// Token::IntegerConstant(n) => Token::StringConstant(Box::new(n.to_string().into())), /// // Convert 'begin' .. 'end' to '{' .. '}' /// Token::Identifier(s) if &*s == "begin" => Token::LeftBrace, /// Token::Identifier(s) if &*s == "end" => Token::RightBrace, From 335d12e182ed5d604ee1f12b0176727591542b8b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Sep 2022 12:19:45 +0800 Subject: [PATCH 009/116] Move Variant into separate file. --- src/api/events.rs | 1 + src/types/dynamic.rs | 105 ++----------------------------------------- src/types/mod.rs | 2 + src/types/variant.rs | 105 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 102 deletions(-) create mode 100644 src/types/variant.rs diff --git a/src/api/events.rs b/src/api/events.rs index 42511be3..a44ee2a4 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -169,6 +169,7 @@ impl Engine { /// let mut engine = Engine::new(); /// /// // Register a token mapper. + /// # #[allow(deprecated)] /// engine.on_parse_token(|token, _, _| { /// match token { /// // Convert all integer literals to strings diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index b310b302..c1fe1dcf 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1,7 +1,5 @@ -//! Helper module which defines the [`Dynamic`] data type and the -//! [`Any`] trait to to allow custom type handling. +//! Helper module which defines the [`Dynamic`] data type. -use crate::func::SendSync; use crate::{reify, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -14,6 +12,8 @@ use std::{ str::FromStr, }; +pub use super::Variant; + #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] pub use std::time::Instant; @@ -26,105 +26,6 @@ pub use instant::Instant; #[allow(dead_code)] const CHECKED: &str = "data type was checked"; -mod private { - use crate::func::SendSync; - use std::any::Any; - - /// A sealed trait that prevents other crates from implementing [`Variant`][super::Variant]. - pub trait Sealed {} - - impl Sealed for T {} -} - -/// _(internals)_ Trait to represent any type. -/// Exported under the `internals` feature only. -/// -/// This trait is sealed and cannot be implemented. -/// -/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type. -/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`]. -#[cfg(not(feature = "sync"))] -pub trait Variant: Any + private::Sealed { - /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. - #[must_use] - fn as_any(&self) -> &dyn Any; - - /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. - #[must_use] - fn as_any_mut(&mut self) -> &mut dyn Any; - - /// Convert this [`Variant`] trait object to [`Box`][Any]. - #[must_use] - fn as_boxed_any(self: Box) -> Box; - - /// Get the name of this type. - #[must_use] - fn type_name(&self) -> &'static str; - - /// Clone this [`Variant`] trait object. - #[must_use] - fn clone_object(&self) -> Box; -} - -/// _(internals)_ Trait to represent any type. -/// Exported under the `internals` feature only. -/// -/// This trait is sealed and cannot be implemented. -#[cfg(feature = "sync")] -pub trait Variant: Any + Send + Sync + private::Sealed { - /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. - #[must_use] - fn as_any(&self) -> &dyn Any; - - /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. - #[must_use] - fn as_any_mut(&mut self) -> &mut dyn Any; - - /// Convert this [`Variant`] trait object to [`Box`][Any]. - #[must_use] - fn as_boxed_any(self: Box) -> Box; - - /// Get the name of this type. - #[must_use] - fn type_name(&self) -> &'static str; - - /// Clone this [`Variant`] trait object. - #[must_use] - fn clone_object(&self) -> Box; -} - -impl Variant for T { - #[inline(always)] - fn as_any(&self) -> &dyn Any { - self - } - #[inline(always)] - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - #[inline(always)] - fn as_boxed_any(self: Box) -> Box { - self - } - #[inline(always)] - fn type_name(&self) -> &'static str { - type_name::() - } - #[inline(always)] - fn clone_object(&self) -> Box { - Box::new(self.clone()) as Box - } -} - -impl dyn Variant { - /// Is this [`Variant`] a specific type? - #[inline(always)] - #[must_use] - pub fn is(&self) -> bool { - TypeId::of::() == self.type_id() - } -} - /// _(internals)_ Modes of access. /// Exported under the `internals` feature only. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] diff --git a/src/types/mod.rs b/src/types/mod.rs index 347bc85c..475ca2c9 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -9,6 +9,7 @@ pub mod immutable_string; pub mod interner; pub mod parse_error; pub mod scope; +pub mod variant; pub use bloom_filter::BloomFilterU64; pub use custom_types::{CustomTypeInfo, CustomTypesCollection}; @@ -21,3 +22,4 @@ pub use immutable_string::ImmutableString; pub use interner::StringsInterner; pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use scope::Scope; +pub use variant::Variant; diff --git a/src/types/variant.rs b/src/types/variant.rs new file mode 100644 index 00000000..0e1d4e6c --- /dev/null +++ b/src/types/variant.rs @@ -0,0 +1,105 @@ +//! [`Variant`] trait to to allow custom type handling. + +use crate::func::SendSync; +use std::any::{type_name, Any, TypeId}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +mod private { + use crate::func::SendSync; + use std::any::Any; + + /// A sealed trait that prevents other crates from implementing [`Variant`][super::Variant]. + pub trait Sealed {} + + impl Sealed for T {} +} + +/// _(internals)_ Trait to represent any type. +/// Exported under the `internals` feature only. +/// +/// This trait is sealed and cannot be implemented. +/// +/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type. +/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`]. +#[cfg(not(feature = "sync"))] +pub trait Variant: Any + private::Sealed { + /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. + #[must_use] + fn as_any(&self) -> &dyn Any; + + /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. + #[must_use] + fn as_any_mut(&mut self) -> &mut dyn Any; + + /// Convert this [`Variant`] trait object to [`Box`][Any]. + #[must_use] + fn as_boxed_any(self: Box) -> Box; + + /// Get the name of this type. + #[must_use] + fn type_name(&self) -> &'static str; + + /// Clone this [`Variant`] trait object. + #[must_use] + fn clone_object(&self) -> Box; +} + +/// _(internals)_ Trait to represent any type. +/// Exported under the `internals` feature only. +/// +/// This trait is sealed and cannot be implemented. +#[cfg(feature = "sync")] +pub trait Variant: Any + Send + Sync + private::Sealed { + /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. + #[must_use] + fn as_any(&self) -> &dyn Any; + + /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. + #[must_use] + fn as_any_mut(&mut self) -> &mut dyn Any; + + /// Convert this [`Variant`] trait object to [`Box`][Any]. + #[must_use] + fn as_boxed_any(self: Box) -> Box; + + /// Get the name of this type. + #[must_use] + fn type_name(&self) -> &'static str; + + /// Clone this [`Variant`] trait object. + #[must_use] + fn clone_object(&self) -> Box; +} + +impl Variant for T { + #[inline(always)] + fn as_any(&self) -> &dyn Any { + self + } + #[inline(always)] + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + #[inline(always)] + fn as_boxed_any(self: Box) -> Box { + self + } + #[inline(always)] + fn type_name(&self) -> &'static str { + type_name::() + } + #[inline(always)] + fn clone_object(&self) -> Box { + Box::new(self.clone()) as Box + } +} + +impl dyn Variant { + /// Is this [`Variant`] a specific type? + #[inline(always)] + #[must_use] + pub fn is(&self) -> bool { + TypeId::of::() == self.type_id() + } +} From 8d1310c0f30141246ecd21acaaa375d994801da9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Sep 2022 18:14:45 +0800 Subject: [PATCH 010/116] Make Scope serializable. --- CHANGELOG.md | 4 ++ src/serde/de.rs | 86 +++++++++++++++++++--------------------- src/serde/deserialize.rs | 81 +++++++++++++++++++++++++++++++------ src/serde/serialize.rs | 34 +++++++++++++++- src/types/scope.rs | 32 ++++++++++++++- tests/serde.rs | 33 ++++++++++++++- 6 files changed, 208 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a057eebe..d95bc3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,12 @@ Version 1.11.0 ============== Enhancements +------------ * The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers. +* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. +* `Scope` is now serializable and deserializable via `serde`. +* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline. Version 1.10.1 diff --git a/src/serde/de.rs b/src/serde/de.rs index dbf43605..8f4cd237 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -9,9 +9,7 @@ use std::prelude::v1::*; use std::{any::type_name, fmt}; /// Deserializer for [`Dynamic`][crate::Dynamic]. -pub struct DynamicDeserializer<'de> { - value: &'de Dynamic, -} +pub struct DynamicDeserializer<'de>(&'de Dynamic); impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic { type Deserializer = DynamicDeserializer<'de>; @@ -19,7 +17,7 @@ impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic { #[inline(always)] #[must_use] fn into_deserializer(self) -> Self::Deserializer { - DynamicDeserializer { value: self } + DynamicDeserializer(self) } } @@ -31,7 +29,7 @@ impl<'de> DynamicDeserializer<'de> { #[inline(always)] #[must_use] pub const fn new(value: &'de Dynamic) -> Self { - Self { value } + Self(value) } /// Shortcut for a type conversion error. #[cold] @@ -43,12 +41,10 @@ impl<'de> DynamicDeserializer<'de> { #[cold] #[inline(never)] fn type_error_str(&self, error: &str) -> RhaiResultOf { - Err(ERR::ErrorMismatchOutputType( - error.into(), - self.value.type_name().into(), - Position::NONE, + Err( + ERR::ErrorMismatchOutputType(error.into(), self.0.type_name().into(), Position::NONE) + .into(), ) - .into()) } #[inline(always)] fn deserialize_int>(self, v: crate::INT, visitor: V) -> RhaiResultOf { @@ -128,7 +124,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { type Error = RhaiError; fn deserialize_any>(self, visitor: V) -> RhaiResultOf { - match self.value.0 { + match self.0 .0 { Union::Unit(..) => self.deserialize_unit(visitor), Union::Bool(..) => self.deserialize_bool(visitor), Union::Str(..) => self.deserialize_str(visitor), @@ -182,110 +178,110 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { } fn deserialize_bool>(self, visitor: V) -> RhaiResultOf { - visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?) + visitor.visit_bool(self.0.as_bool().or_else(|_| self.type_error())?) } fn deserialize_i8>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x)) } } fn deserialize_i16>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x)) } } fn deserialize_i32>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else if cfg!(feature = "only_i32") { self.type_error() } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x)) } } fn deserialize_i64>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else if cfg!(not(feature = "only_i32")) { self.type_error() } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x)) } } fn deserialize_i128>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else if cfg!(not(feature = "only_i32")) { self.type_error() } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x)) } } fn deserialize_u8>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x)) } } fn deserialize_u16>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x)) } } fn deserialize_u32>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x)) } } fn deserialize_u64>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x)) } } fn deserialize_u128>(self, visitor: V) -> RhaiResultOf { - if let Ok(v) = self.value.as_int() { + if let Ok(v) = self.0.as_int() { self.deserialize_int(v, visitor) } else { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x)) } @@ -294,7 +290,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_f32>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_float"))] return self - .value + .0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); @@ -304,7 +300,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { use rust_decimal::prelude::ToPrimitive; return self - .value + .0 .downcast_ref::() .and_then(|&x| x.to_f32()) .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); @@ -318,7 +314,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_f64>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_float"))] return self - .value + .0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); @@ -328,7 +324,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { use rust_decimal::prelude::ToPrimitive; return self - .value + .0 .downcast_ref::() .and_then(|&x| x.to_f64()) .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); @@ -340,13 +336,13 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { } fn deserialize_char>(self, visitor: V) -> RhaiResultOf { - self.value + self.0 .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_char(x)) } fn deserialize_str>(self, visitor: V) -> RhaiResultOf { - self.value.downcast_ref::().map_or_else( + self.0.downcast_ref::().map_or_else( || self.type_error(), |x| visitor.visit_borrowed_str(x.as_str()), ) @@ -359,7 +355,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_bytes>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return self - .value + .0 .downcast_ref::() .map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x)); @@ -372,7 +368,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { } fn deserialize_option>(self, visitor: V) -> RhaiResultOf { - if self.value.is::<()>() { + if self.0.is::<()>() { visitor.visit_none() } else { visitor.visit_some(self) @@ -380,7 +376,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { } fn deserialize_unit>(self, visitor: V) -> RhaiResultOf { - self.value + self.0 .downcast_ref::<()>() .map_or_else(|| self.type_error(), |_| visitor.visit_unit()) } @@ -403,7 +399,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_seq>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] - return self.value.downcast_ref::().map_or_else( + return self.0.downcast_ref::().map_or_else( || self.type_error(), |arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())), ); @@ -427,7 +423,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_map>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] - return self.value.downcast_ref::().map_or_else( + return self.0.downcast_ref::().map_or_else( || self.type_error(), |map| { _visitor.visit_map(IterateMap::new( @@ -456,11 +452,11 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { _variants: &'static [&'static str], visitor: V, ) -> RhaiResultOf { - if let Some(s) = self.value.read_lock::() { + if let Some(s) = self.0.read_lock::() { visitor.visit_enum(s.as_str().into_deserializer()) } else { #[cfg(not(feature = "no_object"))] - return self.value.downcast_ref::().map_or_else( + return self.0.downcast_ref::().map_or_else( || self.type_error(), |map| { let mut iter = map.iter(); diff --git a/src/serde/deserialize.rs b/src/serde/deserialize.rs index a41f8882..e6c5ae3a 100644 --- a/src/serde/deserialize.rs +++ b/src/serde/deserialize.rs @@ -1,14 +1,17 @@ //! Implementations of [`serde::Deserialize`]. -use crate::{Dynamic, ImmutableString, INT}; -use serde::de::{Deserialize, Deserializer, Error, Visitor}; +use crate::{Dynamic, Identifier, ImmutableString, Scope, INT}; +use serde::{ + de::{Error, SeqAccess, Visitor}, + Deserialize, Deserializer, +}; use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; struct DynamicVisitor; -impl<'d> Visitor<'d> for DynamicVisitor { +impl<'de> Visitor<'de> for DynamicVisitor { type Value = Dynamic; #[cold] @@ -145,12 +148,12 @@ impl<'d> Visitor<'d> for DynamicVisitor { } #[inline(always)] - fn visit_newtype_struct>(self, de: D) -> Result { + fn visit_newtype_struct>(self, de: D) -> Result { Deserialize::deserialize(de) } #[cfg(not(feature = "no_index"))] - fn visit_seq>(self, mut seq: A) -> Result { + fn visit_seq>(self, mut seq: A) -> Result { let mut arr = crate::Array::new(); while let Some(v) = seq.next_element()? { @@ -161,7 +164,7 @@ impl<'d> Visitor<'d> for DynamicVisitor { } #[cfg(not(feature = "no_object"))] - fn visit_map>(self, mut map: M) -> Result { + fn visit_map>(self, mut map: M) -> Result { let mut m = crate::Map::new(); while let Some((k, v)) = map.next_entry()? { @@ -172,17 +175,69 @@ impl<'d> Visitor<'d> for DynamicVisitor { } } -impl<'d> Deserialize<'d> for Dynamic { +impl<'de> Deserialize<'de> for Dynamic { #[inline(always)] - fn deserialize>(de: D) -> Result { - de.deserialize_any(DynamicVisitor) + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_any(DynamicVisitor) } } -impl<'d> Deserialize<'d> for ImmutableString { - #[inline] - fn deserialize>(de: D) -> Result { - let s: String = Deserialize::deserialize(de)?; +impl<'de> Deserialize<'de> for ImmutableString { + #[inline(always)] + fn deserialize>(deserializer: D) -> Result { + let s: String = Deserialize::deserialize(deserializer)?; Ok(s.into()) } } + +impl<'de> Deserialize<'de> for Scope<'de> { + #[inline(always)] + fn deserialize>(deserializer: D) -> Result { + #[derive(Debug, Clone, Hash, Deserialize)] + struct ScopeEntry { + pub name: Identifier, + pub value: Dynamic, + #[serde(default)] + pub is_constant: bool, + } + + struct VecVisitor; + + impl<'de> Visitor<'de> for VecVisitor { + type Value = Scope<'static>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a sequence") + } + + #[inline] + fn visit_seq(self, mut access: A) -> Result + where + A: SeqAccess<'de>, + { + let mut scope = if let Some(size) = access.size_hint() { + Scope::with_capacity(size) + } else { + Scope::new() + }; + + while let Some(ScopeEntry { + name, + value, + is_constant, + }) = access.next_element()? + { + if is_constant { + scope.push_constant_dynamic(name, value); + } else { + scope.push_dynamic(name, value); + } + } + + Ok(scope) + } + } + + deserializer.deserialize_seq(VecVisitor) + } +} diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 4aaa797e..818b0e9e 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -1,8 +1,8 @@ //! Implementations of [`serde::Serialize`]. use crate::types::dynamic::Union; -use crate::{Dynamic, ImmutableString}; -use serde::ser::{Serialize, Serializer}; +use crate::{Dynamic, ImmutableString, Scope}; +use serde::{ser::SerializeSeq, Serialize, Serializer}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -88,3 +88,33 @@ impl Serialize for ImmutableString { ser.serialize_str(self.as_str()) } } + +impl Serialize for Scope<'_> { + #[inline(always)] + fn serialize(&self, ser: S) -> Result { + #[derive(Debug, Clone, Hash, Serialize)] + struct ScopeEntry<'a> { + pub name: &'a str, + pub value: &'a Dynamic, + #[serde(default, skip_serializing_if = "is_false")] + pub is_constant: bool, + } + + fn is_false(value: &bool) -> bool { + !value + } + + let mut ser = ser.serialize_seq(Some(self.len()))?; + + for (name, is_constant, value) in self.iter_raw() { + let entry = ScopeEntry { + name, + value, + is_constant, + }; + ser.serialize_element(&entry)?; + } + + ser.end() + } +} diff --git a/src/types/scope.rs b/src/types/scope.rs index 46dd1d1f..4cc5a91c 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -22,6 +22,14 @@ const SCOPE_ENTRIES_INLINED: usize = 8; /// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for /// future versions. Until then, `'static` can be used. /// +/// # Constant Generic Parameter +/// +/// There is a constant generic parameter that indicates how many entries to keep inline. +/// As long as the number of entries does not exceed this limit, no allocations occur. +/// The default is 8. +/// +/// A larger value makes [`Scope`] larger, but reduces the chance of allocations. +/// /// # Thread Safety /// /// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it @@ -61,7 +69,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8; // // [`Dynamic`] is reasonably small so packing it tightly improves cache performance. #[derive(Debug, Hash, Default)] -pub struct Scope<'a> { +pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> { /// Current value of the entry. values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, /// Name of the entry. @@ -170,6 +178,28 @@ impl Scope<'_> { dummy: PhantomData, } } + /// Create a new [`Scope`] with a particular capacity. + /// + /// # Example + /// + /// ``` + /// use rhai::Scope; + /// + /// let mut my_scope = Scope::with_capacity(10); + /// + /// my_scope.push("x", 42_i64); + /// assert_eq!(my_scope.get_value::("x").expect("x should exist"), 42); + /// ``` + #[inline(always)] + #[must_use] + pub fn with_capacity(capacity: usize) -> Self { + Self { + values: SmallVec::with_capacity(capacity), + names: SmallVec::with_capacity(capacity), + aliases: SmallVec::with_capacity(capacity), + dummy: PhantomData, + } + } /// Empty the [`Scope`]. /// /// # Example diff --git a/tests/serde.rs b/tests/serde.rs index 45c16b72..4a08bb81 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -2,7 +2,7 @@ use rhai::{ serde::{from_dynamic, to_dynamic}, - Dynamic, Engine, EvalAltResult, ImmutableString, INT, + Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT, }; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -823,3 +823,34 @@ fn test_serde_json_borrowed_string() { println!("value: {value:?}"); let _: Dynamic = serde_json::from_value(value).unwrap(); } + +#[test] +#[cfg(not(feature = "no_object"))] +fn test_serde_scope() { + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + struct TestStruct { + foo: Option, + } + + let mut scope = Scope::new(); + scope.push("x", 42 as INT); + scope.push_constant("y", true); + scope.push("z", TestStruct { foo: None }); + + let json = serde_json::to_string(&scope).unwrap(); + + assert_eq!( + json, + r#"[{"name":"x","value":42},{"name":"y","value":true,"is_constant":true},{"name":"z","value":"serde::test_serde_scope::TestStruct"}]"# + ); + + scope = serde_json::from_str(&json).unwrap(); + + assert_eq!(scope.len(), 3); + assert_eq!(scope.get_value::("x").unwrap(), 42); + assert_eq!(scope.get_value::("y").unwrap(), true); + assert_eq!( + scope.get_value::("z").unwrap(), + "serde::test_serde_scope::TestStruct" + ); +} From 8f1cb4aef7c3261c01fbdfd19cc7236a937c63a4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Sep 2022 22:12:28 +0800 Subject: [PATCH 011/116] Use string interpolation. --- src/types/error.rs | 92 ++++++++++++++++++---------------------- src/types/parse_error.rs | 82 ++++++++++++++++------------------- 2 files changed, 79 insertions(+), 95 deletions(-) diff --git a/src/types/error.rs b/src/types/error.rs index 822b1a39..ba9b7351 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -126,47 +126,45 @@ impl Error for EvalAltResult {} impl fmt::Display for EvalAltResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::ErrorSystem(s, err) => match s.as_str() { - "" => write!(f, "{}", err), - s => write!(f, "{}: {}", s, err), - }?, + Self::ErrorSystem(s, err) if s.is_empty() => write!(f, "{err}")?, + Self::ErrorSystem(s, err) => write!(f, "{s}: {err}")?, - Self::ErrorParsing(p, ..) => write!(f, "Syntax error: {}", p)?, + Self::ErrorParsing(p, ..) => write!(f, "Syntax error: {p}")?, #[cfg(not(feature = "no_function"))] Self::ErrorInFunctionCall(s, src, err, ..) if crate::parser::is_anonymous_fn(s) => { - write!(f, "{} in call to closure", err)?; + write!(f, "{err} in call to closure")?; if !src.is_empty() { - write!(f, " @ '{}'", src)?; + write!(f, " @ '{src}'")?; } } Self::ErrorInFunctionCall(s, src, err, ..) => { - write!(f, "{} in call to function {}", err, s)?; + write!(f, "{err} in call to function {s}")?; if !src.is_empty() { - write!(f, " @ '{}'", src)?; + write!(f, " @ '{src}'")?; } } Self::ErrorInModule(s, err, ..) if s.is_empty() => { - write!(f, "Error in module > {}", err)? + write!(f, "Error in module > {err}")? } - Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{}' > {}", s, err)?, + Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{s}' > {err}")?, - Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {}", s)?, - Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {}", s)?, - Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {}", s)?, - Self::ErrorPropertyNotFound(s, ..) => write!(f, "Property not found: {}", s)?, - Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {}", s)?, - Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {}", s)?, - Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {}", s)?, + Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {s}")?, + Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {s}")?, + Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {s}")?, + Self::ErrorPropertyNotFound(s, ..) => write!(f, "Property not found: {s}")?, + Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {s}")?, + Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {s}")?, + Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {s}")?, Self::ErrorDataRace(s, ..) => { - write!(f, "Data race detected when accessing variable: {}", s)? + write!(f, "Data race detected when accessing variable: {s}")? } - Self::ErrorDotExpr(s, ..) => match s.as_str() { - "" => f.write_str("Malformed dot expression"), - s => f.write_str(s), - }?, - Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {}", s)?, + + Self::ErrorDotExpr(s, ..) if s.is_empty() => f.write_str("Malformed dot expression")?, + Self::ErrorDotExpr(s, ..) => f.write_str(s)?, + + Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {s}")?, Self::ErrorUnboundThis(..) => f.write_str("'this' not bound")?, Self::ErrorFor(..) => f.write_str("For loop expects an iterable type")?, Self::ErrorTooManyOperations(..) => f.write_str("Too many operations")?, @@ -181,63 +179,57 @@ impl fmt::Display for EvalAltResult { { write!(f, "Runtime error")? } - Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {}", d)?, + Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {d}")?, - Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?, + Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {s}")?, Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) { - ("", e) => write!(f, "Output type incorrect, expecting {}", e), - (a, "") => write!(f, "Output type incorrect: {}", a), - (a, e) => write!(f, "Output type incorrect: {} (expecting {})", a, e), + ("", e) => write!(f, "Output type incorrect, expecting {e}"), + (a, "") => write!(f, "Output type incorrect: {a}"), + (a, e) => write!(f, "Output type incorrect: {a} (expecting {e})"), }?, Self::ErrorMismatchDataType(e, a, ..) => match (a.as_str(), e.as_str()) { - ("", e) => write!(f, "Data type incorrect, expecting {}", e), - (a, "") => write!(f, "Data type incorrect: {}", a), - (a, e) => write!(f, "Data type incorrect: {} (expecting {})", a, e), - }?, - Self::ErrorArithmetic(s, ..) => match s.as_str() { - "" => f.write_str("Arithmetic error"), - s => f.write_str(s), + ("", e) => write!(f, "Data type incorrect, expecting {e}"), + (a, "") => write!(f, "Data type incorrect: {a}"), + (a, e) => write!(f, "Data type incorrect: {a} (expecting {e})"), }?, + Self::ErrorArithmetic(s, ..) if s.is_empty() => f.write_str("Arithmetic error")?, + Self::ErrorArithmetic(s, ..) => f.write_str(s)?, + Self::LoopBreak(true, ..) => f.write_str("'break' not inside a loop")?, Self::LoopBreak(false, ..) => f.write_str("'continue' not inside a loop")?, Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?, Self::ErrorArrayBounds(max, index, ..) => match max { - 0 => write!(f, "Array index {} out of bounds: array is empty", index), + 0 => write!(f, "Array index {index} out of bounds: array is empty"), 1 => write!( f, - "Array index {} out of bounds: only 1 element in array", - index + "Array index {index} out of bounds: only 1 element in array", ), _ => write!( f, - "Array index {} out of bounds: only {} elements in array", - index, max + "Array index {index} out of bounds: only {max} elements in array", ), }?, Self::ErrorStringBounds(max, index, ..) => match max { - 0 => write!(f, "String index {} out of bounds: string is empty", index), + 0 => write!(f, "String index {index} out of bounds: string is empty"), 1 => write!( f, - "String index {} out of bounds: only 1 character in string", - index + "String index {index} out of bounds: only 1 character in string", ), _ => write!( f, - "String index {} out of bounds: only {} characters in string", - index, max + "String index {index} out of bounds: only {max} characters in string", ), }?, Self::ErrorBitFieldBounds(max, index, ..) => write!( f, - "Bit-field index {} out of bounds: only {} bits in bit-field", - index, max + "Bit-field index {index} out of bounds: only {max} bits in bit-field", )?, - Self::ErrorDataTooLarge(typ, ..) => write!(f, "{} exceeds maximum limit", typ)?, + Self::ErrorDataTooLarge(typ, ..) => write!(f, "{typ} exceeds maximum limit")?, - Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{}: {}", s, tokens.join(" "))?, + Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{s}: {}", tokens.join(" "))?, } // Do not write any position if None diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index e9b55ce3..7ac362af 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -184,77 +184,69 @@ impl ParseErrorType { impl fmt::Display for ParseErrorType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::BadInput(err) => write!(f, "{}", err), + Self::BadInput(err) => write!(f, "{err}"), - Self::UnknownOperator(s) => write!(f, "Unknown operator: '{}'", s), + Self::UnknownOperator(s) => write!(f, "Unknown operator: '{s}'"), - Self::MalformedCallExpr(s) => match s.as_str() { - "" => f.write_str("Invalid expression in function call arguments"), - s => f.write_str(s) - }, - Self::MalformedIndexExpr(s) => match s.as_str() { - "" => f.write_str("Invalid index in indexing expression"), - s => f.write_str(s) - }, - Self::MalformedInExpr(s) => match s.as_str() { - "" => f.write_str("Invalid 'in' expression"), - s => f.write_str(s) - }, - Self::MalformedCapture(s) => match s.as_str() { - "" => f.write_str("Invalid capturing"), - s => f.write_str(s) - }, + Self::MalformedCallExpr(s) if s.is_empty() => f.write_str(s), + Self::MalformedCallExpr(..) => f.write_str("Invalid expression in function call arguments"), + + Self::MalformedIndexExpr(s) if s.is_empty() => f.write_str("Invalid index in indexing expression"), + Self::MalformedIndexExpr(s) => f.write_str(s), + + Self::MalformedInExpr(s) if s.is_empty() => f.write_str("Invalid 'in' expression"), + Self::MalformedInExpr(s) => f.write_str(s), + + Self::MalformedCapture(s) if s.is_empty() => f.write_str("Invalid capturing"), + Self::MalformedCapture(s) => f.write_str(s), Self::FnDuplicatedDefinition(s, n) => { - write!(f, "Function {} with ", s)?; + write!(f, "Function {s} with ")?; match n { 0 => f.write_str("no parameters already exists"), 1 => f.write_str("1 parameter already exists"), - _ => write!(f, "{} parameters already exists", n), + _ => write!(f, "{n} parameters already exists"), } } - Self::FnMissingBody(s) => match s.as_str() { - "" => f.write_str("Expecting body statement block for anonymous function"), - s => write!(f, "Expecting body statement block for function {}", s) - }, - Self::FnMissingParams(s) => write!(f, "Expecting parameters for function {}", s), - Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter {} for function {}", arg, s), - Self::DuplicatedProperty(s) => write!(f, "Duplicated property for object map literal: {}", s), + Self::FnMissingBody(s) if s.is_empty() => f.write_str("Expecting body statement block for anonymous function"), + Self::FnMissingBody(s) => write!(f, "Expecting body statement block for function {s}"), + + Self::FnMissingParams(s) => write!(f, "Expecting parameters for function {s}"), + Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter {arg} for function {s}"), + + Self::DuplicatedProperty(s) => write!(f, "Duplicated property for object map literal: {s}"), #[allow(deprecated)] Self::DuplicatedSwitchCase => f.write_str("Duplicated switch case"), - Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name: {}", s), + Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name: {s}"), - Self::VariableExists(s) => write!(f, "Variable already defined: {}", s), - Self::VariableUndefined(s) => write!(f, "Undefined variable: {}", s), - Self::ModuleUndefined(s) => write!(f, "Undefined module: {}", s), + Self::VariableExists(s) => write!(f, "Variable already defined: {s}"), + Self::VariableUndefined(s) => write!(f, "Undefined variable: {s}"), + Self::ModuleUndefined(s) => write!(f, "Undefined module: {s}"), - Self::MismatchedType(r, a) => write!(f, "Expecting {}, not {}", r, a), + Self::MismatchedType(r, a) => write!(f, "Expecting {r}, not {a}"), Self::ExprExpected(s) => write!(f, "Expecting {} expression", s), - Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s), + Self::MissingToken(token, s) => write!(f, "Expecting '{token}' {s}"), Self::MissingSymbol(s) if s.is_empty() => f.write_str("Expecting a symbol"), Self::MissingSymbol(s) => f.write_str(s), - Self::AssignmentToConstant(s) => match s.as_str() { - "" => f.write_str("Cannot assign to a constant value"), - s => write!(f, "Cannot assign to constant {}", s) - }, - Self::AssignmentToInvalidLHS(s) => match s.as_str() { - "" => f.write_str("Expression cannot be assigned to"), - s => f.write_str(s) - }, + Self::AssignmentToConstant(s) if s.is_empty() => f.write_str("Cannot assign to a constant value"), + Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant {s}"), - Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max), - Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{}' is a reserved keyword", s), - Self::Reserved(s) => write!(f, "'{}' is a reserved symbol", s), + Self::AssignmentToInvalidLHS(s) if s.is_empty() => f.write_str("Expression cannot be assigned to"), + Self::AssignmentToInvalidLHS(s) => f.write_str(s), + + Self::LiteralTooLarge(typ, max) => write!(f, "{typ} exceeds the maximum limit ({max})"), + Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{s}' is a reserved keyword"), + Self::Reserved(s) => write!(f, "'{s}' is a reserved symbol"), Self::UnexpectedEOF => f.write_str("Script is incomplete"), Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"), Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"), Self::WrongSwitchCaseCondition => f.write_str("This switch case cannot have a condition"), Self::PropertyExpected => f.write_str("Expecting name of a property"), Self::VariableExpected => f.write_str("Expecting name of a variable"), - Self::ForbiddenVariable(s) => write!(f, "Forbidden variable name: {}", s), + Self::ForbiddenVariable(s) => write!(f, "Forbidden variable name: {s}"), Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"), Self::FnMissingName => f.write_str("Expecting function name in function declaration"), Self::WrongDocComment => f.write_str("Doc-comment must be followed immediately by a function definition"), From 3a3653f42ad433ae0f16051dca595a4d8e3e4cd4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Sep 2022 23:35:37 +0800 Subject: [PATCH 012/116] Refactor. --- src/func/builtin.rs | 38 ++++++++++++++++++------------------- src/optimizer.rs | 8 ++++---- src/packages/array_basic.rs | 2 +- src/types/dynamic.rs | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 9052a5fe..075706fe 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -280,7 +280,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Token::GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), Token::LessThan => Some(impl_op!(FLOAT => $xx < $yy)), Token::LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), - _ => None, + _ => None, }; } }; @@ -308,7 +308,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Token::Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), Token::Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), Token::PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), - _ => () + _ => () } #[cfg(feature = "unchecked")] @@ -322,17 +322,17 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Token::Divide => return Some(impl_op!(from Decimal => $xx / $yy)), Token::Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), Token::PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), - _ => () + _ => () } return match op { - Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), - Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), - Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), - Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), - Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)), - Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), - _ => None + Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), + Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), + Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), + Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), + Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)), + Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), + _ => None }; } }; @@ -680,7 +680,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt Token::DivideAssign => Some(impl_op!($x /= $yy)), Token::ModuloAssign => Some(impl_op!($x %= $yy)), Token::PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), - _ => None, + _ => None, }; } } @@ -707,7 +707,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt Token::DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), Token::ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), Token::PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), - _ => None, + _ => None, }; #[cfg(feature = "unchecked")] @@ -715,13 +715,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(feature = "unchecked")] return match op { - Token::PlusAssign => Some(impl_op!(from $x += $yy)), - Token::MinusAssign => Some(impl_op!(from $x -= $yy)), - Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)), - Token::DivideAssign => Some(impl_op!(from $x /= $yy)), - Token::ModuloAssign => Some(impl_op!(from $x %= $yy)), - Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), - _ => None, + Token::PlusAssign => Some(impl_op!(from $x += $yy)), + Token::MinusAssign => Some(impl_op!(from $x -= $yy)), + Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)), + Token::DivideAssign => Some(impl_op!(from $x /= $yy)), + Token::ModuloAssign => Some(impl_op!(from $x %= $yy)), + Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), + _ => None, }; } }; diff --git a/src/optimizer.rs b/src/optimizer.rs index 6ab19fcb..c46cf43f 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -254,7 +254,7 @@ fn optimize_stmt_block( }); // Optimize each statement in the block - for stmt in statements.iter_mut() { + for stmt in &mut statements { match stmt { Stmt::Var(x, options, ..) => { if options.contains(ASTFlags::CONSTANT) { @@ -688,7 +688,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b optimize_expr(match_expr, state, false); // Optimize blocks - for b in expressions.iter_mut() { + for b in expressions.as_mut() { optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.expr, state, false); @@ -1204,7 +1204,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); // Move constant arguments - for arg in x.args.iter_mut() { + for arg in &mut x.args { match arg { Expr::DynamicConstant(..) | Expr::Unit(..) | Expr::StringConstant(..) | Expr::CharConstant(..) @@ -1254,7 +1254,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { } // id(args ..) or xxx.id(args ..) -> optimize function call arguments - Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => for arg in x.args.iter_mut() { + Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => for arg in &mut x.args { optimize_expr(arg, state, false); // Move constant arguments diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 21abbde9..72e2347e 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -834,7 +834,7 @@ pub mod array_functions { return Ok(false); } - for item in array.iter_mut() { + for item in array { if ctx .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .or_else(|err| match *err { diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index c1fe1dcf..db88b9fa 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -928,7 +928,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] Union::Array(ref mut a, _, ref mut access) => { *access = typ; - for v in a.iter_mut() { + for v in a.as_mut() { v.set_access_mode(typ); } } From 20d4b715912a8e73c82c1b0bcbb5ecf09ff47016 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Sep 2022 23:45:50 +0800 Subject: [PATCH 013/116] Add stable_hash. --- CHANGELOG.md | 5 +++++ Cargo.toml | 1 + src/func/hashing.rs | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d95bc3d0..9340630c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.11.0 ============== +New features +------------ + +* A new feature flag, `stable_hash`, is added that forces hashing to be consistent using a fixed seed. + Enhancements ------------ diff --git a/Cargo.toml b/Cargo.toml index 1034f923..6451af51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] internals = [] # expose internal data structures debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types +stable_hash = [] # perform all hashing with fixed seed value # compiling for no-std no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng", "hashbrown/ahash-compile-time-rng"] diff --git a/src/func/hashing.rs b/src/func/hashing.rs index bc29f67b..ae0a57bc 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -74,7 +74,11 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - ahash::AHasher::default() + if cfg!(feature = "stable_hash") { + ahash::RandomState::with_seeds(42, 999, 123, 0).build_hasher() + } else { + ahash::AHasher::default() + } } /// Calculate a non-zero [`u64`] hash key from a namespace-qualified variable name. From 25476d1cea07fa96874dbdfafaf30c2c4e9d1b3a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Sep 2022 08:52:39 +0800 Subject: [PATCH 014/116] Mark debug functions cold. --- src/ast/ast.rs | 2 ++ src/ast/expr.rs | 9 ++++++++- src/ast/ident.rs | 2 ++ src/ast/namespace.rs | 2 ++ src/ast/stmt.rs | 7 ++++++- src/engine.rs | 3 ++- src/eval/global_state.rs | 3 ++- src/func/callable_function.rs | 2 ++ src/module/mod.rs | 2 ++ src/packages/iter_basic.rs | 2 ++ src/tokenizer.rs | 5 ++++- src/types/custom_types.rs | 2 ++ src/types/dynamic.rs | 2 ++ src/types/fn_ptr.rs | 2 ++ src/types/immutable_string.rs | 3 ++- src/types/scope.rs | 1 - 16 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 902a6856..f6ebb50c 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -42,6 +42,8 @@ impl Default for AST { } impl fmt::Debug for AST { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut fp = f.debug_struct("AST"); diff --git a/src/ast/expr.rs b/src/ast/expr.rs index e29745a7..037079e8 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -117,6 +117,8 @@ pub struct FnCallHashes { } impl fmt::Debug for FnCallHashes { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_function"))] if self.script != 0 { @@ -199,6 +201,8 @@ pub struct FnCallExpr { } impl fmt::Debug for FnCallExpr { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut ff = f.debug_struct("FnCallExpr"); #[cfg(not(feature = "no_module"))] @@ -294,7 +298,8 @@ impl DerefMut for FloatWrapper { #[cfg(not(feature = "no_float"))] impl fmt::Debug for FloatWrapper { - #[inline(always)] + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } @@ -448,6 +453,8 @@ impl Default for Expr { } impl fmt::Debug for Expr { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut display_pos = format!(" @ {:?}", self.start_position()); diff --git a/src/ast/ident.rs b/src/ast/ident.rs index 8a6d98aa..eb84c432 100644 --- a/src/ast/ident.rs +++ b/src/ast/ident.rs @@ -20,6 +20,8 @@ pub struct Ident { } impl fmt::Debug for Ident { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.name)?; self.pos.debug_print(f) diff --git a/src/ast/namespace.rs b/src/ast/namespace.rs index 9dba5b18..ee36d3bd 100644 --- a/src/ast/namespace.rs +++ b/src/ast/namespace.rs @@ -29,6 +29,8 @@ pub struct Namespace { } impl fmt::Debug for Namespace { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_empty() { return f.write_str("NONE"); diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 3bbb0177..ba6f0821 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -106,6 +106,8 @@ impl OpAssignment { } impl fmt::Debug for OpAssignment { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_op_assignment() { f.debug_struct("OpAssignment") @@ -178,7 +180,8 @@ pub enum RangeCase { } impl fmt::Debug for RangeCase { - #[inline] + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {}", r.start, r.end, n), @@ -454,6 +457,8 @@ impl AsMut<[Stmt]> for StmtBlock { } impl fmt::Debug for StmtBlock { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Block")?; fmt::Debug::fmt(&self.block, f)?; diff --git a/src/engine.rs b/src/engine.rs index 72f95c6b..fb905cde 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -150,7 +150,8 @@ pub struct Engine { } impl fmt::Debug for Engine { - #[inline] + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("Engine"); diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 775fdda0..20ee2614 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -354,7 +354,8 @@ impl, M: Into>> Ext } impl fmt::Debug for GlobalRuntimeState<'_> { - #[inline] + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("GlobalRuntimeState"); diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index babf1ebf..3973b1b1 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -27,6 +27,8 @@ pub enum CallableFunction { } impl fmt::Debug for CallableFunction { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pure(..) => write!(f, "NativePureFunction"), diff --git a/src/module/mod.rs b/src/module/mod.rs index a07a82d3..fb94e590 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -202,6 +202,8 @@ impl Default for Module { } impl fmt::Debug for Module { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Module"); diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index b9c849fe..4473cfbb 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -47,6 +47,8 @@ pub struct StepRange { } impl Debug for StepRange { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple(&format!("StepRange<{}>", type_name::())) .field(&self.from) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7cea143e..81893066 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -241,6 +241,8 @@ impl fmt::Display for Position { } impl fmt::Debug for Position { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_none() { f.write_str("none") @@ -333,7 +335,6 @@ impl Span { } impl fmt::Display for Span { - #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _f = f; @@ -360,6 +361,8 @@ impl fmt::Display for Span { } impl fmt::Debug for Span { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } diff --git a/src/types/custom_types.rs b/src/types/custom_types.rs index 3b759edc..ca43e1e8 100644 --- a/src/types/custom_types.rs +++ b/src/types/custom_types.rs @@ -17,6 +17,8 @@ pub struct CustomTypeInfo { pub struct CustomTypesCollection(BTreeMap); impl fmt::Debug for CustomTypesCollection { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("CustomTypesCollection ")?; f.debug_map().entries(self.0.iter()).finish() diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index db88b9fa..79e63abf 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -507,6 +507,8 @@ impl fmt::Display for Dynamic { } impl fmt::Debug for Dynamic { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f), diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 3ac75077..ab39e9b9 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -23,6 +23,8 @@ pub struct FnPtr { } impl fmt::Debug for FnPtr { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_curried() { self.curry diff --git a/src/types/immutable_string.rs b/src/types/immutable_string.rs index 507c33d6..7353f151 100644 --- a/src/types/immutable_string.rs +++ b/src/types/immutable_string.rs @@ -197,7 +197,8 @@ impl fmt::Display for ImmutableString { } impl fmt::Debug for ImmutableString { - #[inline(always)] + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.as_str(), f) } diff --git a/src/types/scope.rs b/src/types/scope.rs index 4cc5a91c..169830b4 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -81,7 +81,6 @@ pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> { } impl fmt::Display for Scope<'_> { - #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (i, (name, constant, value)) in self.iter_raw().enumerate() { #[cfg(not(feature = "no_closure"))] From a518ab62bb22f8055a2b8aa89d10b5779839984d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Sep 2022 08:52:51 +0800 Subject: [PATCH 015/116] Simplify strings interner. --- src/parser.rs | 25 ++++++++------ src/types/interner.rs | 76 ++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 9941fc53..6cdf3d18 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -47,9 +47,6 @@ const NEVER_ENDS: &str = "`Token`"; /// Unroll `switch` ranges no larger than this. const SMALL_SWITCH_RANGE: INT = 16; -/// Number of string interners used: two additional for property getters/setters if not `no_object` -const NUM_INTERNERS: usize = if cfg!(feature = "no_object") { 1 } else { 3 }; - /// _(internals)_ A type that encapsulates the current state of the parser. /// Exported under the `internals` feature only. pub struct ParseState<'e> { @@ -58,7 +55,7 @@ pub struct ParseState<'e> { /// Controls whether parsing of an expression should stop given the next token. pub expr_filter: fn(&Token) -> bool, /// String interners. - interned_strings: [StringsInterner<'e>; NUM_INTERNERS], + interned_strings: StringsInterner<'e>, /// External [scope][Scope] with constants. pub scope: &'e Scope<'e>, /// Global runtime state. @@ -88,6 +85,8 @@ pub struct ParseState<'e> { } impl fmt::Debug for ParseState<'_> { + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("ParseState"); @@ -116,7 +115,7 @@ impl<'e> ParseState<'e> { pub fn new( engine: &Engine, scope: &'e Scope, - interned_strings: [StringsInterner<'e>; NUM_INTERNERS], + interned_strings: StringsInterner<'e>, tokenizer_control: TokenizerControl, ) -> Self { Self { @@ -254,7 +253,7 @@ impl<'e> ParseState<'e> { &mut self, text: impl AsRef + Into, ) -> ImmutableString { - self.interned_strings[0].get(text) + self.interned_strings.get(text) } /// Get an interned property getter, creating one if it is not yet interned. @@ -265,8 +264,11 @@ impl<'e> ParseState<'e> { &mut self, text: impl AsRef + Into, ) -> ImmutableString { - self.interned_strings[1] - .get_with_mapper(|s| crate::engine::make_getter(s.as_ref()).into(), text) + self.interned_strings.get_with_mapper( + crate::engine::FN_GET, + |s| crate::engine::make_getter(s.as_ref()).into(), + text, + ) } /// Get an interned property setter, creating one if it is not yet interned. @@ -277,8 +279,11 @@ impl<'e> ParseState<'e> { &mut self, text: impl AsRef + Into, ) -> ImmutableString { - self.interned_strings[2] - .get_with_mapper(|s| crate::engine::make_setter(s.as_ref()).into(), text) + self.interned_strings.get_with_mapper( + crate::engine::FN_SET, + |s| crate::engine::make_setter(s.as_ref()).into(), + text, + ) } } diff --git a/src/types/interner.rs b/src/types/interner.rs index 3dfa1048..7ae35a35 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,3 +1,4 @@ +use super::BloomFilterU64; use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::ImmutableString; #[cfg(feature = "no_std")] @@ -14,7 +15,7 @@ use std::{ }; /// Maximum number of strings interned. -pub const MAX_INTERNED_STRINGS: usize = 256; +pub const MAX_INTERNED_STRINGS: usize = 1024; /// Maximum length of strings interned. pub const MAX_STRING_LEN: usize = 24; @@ -28,8 +29,10 @@ pub struct StringsInterner<'a> { pub capacity: usize, /// Maximum string length. pub max_string_len: usize, - /// Normal strings. - strings: StraightHashMap, + /// Cached strings. + cache: StraightHashMap, + /// Bloom filter to avoid caching "one-hit wonders". + filter: BloomFilterU64, /// Take care of the lifetime parameter. dummy: PhantomData<&'a ()>, } @@ -42,9 +45,10 @@ impl Default for StringsInterner<'_> { } impl fmt::Debug for StringsInterner<'_> { - #[inline] + #[cold] + #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.strings.values()).finish() + f.debug_list().entries(self.cache.values()).finish() } } @@ -56,7 +60,8 @@ impl StringsInterner<'_> { Self { capacity: MAX_INTERNED_STRINGS, max_string_len: MAX_STRING_LEN, - strings: StraightHashMap::default(), + cache: StraightHashMap::default(), + filter: BloomFilterU64::new(), dummy: PhantomData, } } @@ -65,7 +70,7 @@ impl StringsInterner<'_> { #[inline(always)] #[must_use] pub fn get + Into>(&mut self, text: S) -> ImmutableString { - self.get_with_mapper(Into::into, text) + self.get_with_mapper("", Into::into, text) } /// Get an identifier from a text string, adding it to the interner if necessary. @@ -73,20 +78,23 @@ impl StringsInterner<'_> { #[must_use] pub fn get_with_mapper>( &mut self, + id: &str, mapper: impl Fn(S) -> ImmutableString, text: S, ) -> ImmutableString { let key = text.as_ref(); - if key.len() > MAX_STRING_LEN { + let hasher = &mut get_hasher(); + id.hash(hasher); + key.hash(hasher); + let hash = hasher.finish(); + + // Cache long strings only on the second try to avoid caching "one-hit wonders". + if key.len() > MAX_STRING_LEN && self.filter.is_absent_and_set(hash) { return mapper(text); } - let hasher = &mut get_hasher(); - key.hash(hasher); - let key = hasher.finish(); - - let result = match self.strings.entry(key) { + let result = match self.cache.entry(hash) { Entry::Occupied(e) => return e.get().clone(), Entry::Vacant(e) => { let value = mapper(text); @@ -100,7 +108,7 @@ impl StringsInterner<'_> { }; // If the interner is over capacity, remove the longest entry that has the lowest count - if self.strings.len() > self.capacity { + if self.cache.len() > self.capacity { // Leave some buffer to grow when shrinking the cache. // We leave at least two entries, one for the empty string, and one for the string // that has just been inserted. @@ -110,21 +118,21 @@ impl StringsInterner<'_> { self.capacity - 3 }; - while self.strings.len() > max { - let (_, _, n) = - self.strings - .iter() - .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { - if k != key - && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) - { - (v.len(), v.strong_count(), k) - } else { - (x, c, n) - } - }); + while self.cache.len() > max { + let (_, _, n) = self + .cache + .iter() + .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { + if k != hash + && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) + { + (v.len(), v.strong_count(), k) + } else { + (x, c, n) + } + }); - self.strings.remove(&n); + self.cache.remove(&n); } } @@ -136,7 +144,7 @@ impl StringsInterner<'_> { #[must_use] #[allow(dead_code)] pub fn len(&self) -> usize { - self.strings.len() + self.cache.len() } /// Returns `true` if there are no interned strings. @@ -144,28 +152,28 @@ impl StringsInterner<'_> { #[must_use] #[allow(dead_code)] pub fn is_empty(&self) -> bool { - self.strings.is_empty() + self.cache.is_empty() } /// Clear all interned strings. #[inline(always)] #[allow(dead_code)] pub fn clear(&mut self) { - self.strings.clear(); + self.cache.clear(); } } impl AddAssign for StringsInterner<'_> { #[inline(always)] fn add_assign(&mut self, rhs: Self) { - self.strings.extend(rhs.strings.into_iter()); + self.cache.extend(rhs.cache.into_iter()); } } impl AddAssign<&Self> for StringsInterner<'_> { #[inline(always)] fn add_assign(&mut self, rhs: &Self) { - self.strings - .extend(rhs.strings.iter().map(|(&k, v)| (k, v.clone()))); + self.cache + .extend(rhs.cache.iter().map(|(&k, v)| (k, v.clone()))); } } From 2ecf44a48e791e1576586075477c88ad635d2af1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Sep 2022 13:23:47 +0800 Subject: [PATCH 016/116] Remove stable_hash feature and use environment variable. --- CHANGELOG.md | 4 +++- Cargo.toml | 1 - build.rs | 27 +++++++++++++++++++++++++++ src/config.rs | 4 ++++ src/func/hashing.rs | 5 +++-- src/lib.rs | 7 ++++--- 6 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 build.rs create mode 100644 src/config.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9340630c..70fc8550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ Version 1.11.0 New features ------------ -* A new feature flag, `stable_hash`, is added that forces hashing to be consistent using a fixed seed. +### Stable hashing + +* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher in order to force stable (i.e. deterministic) hashes for function signatures. Enhancements ------------ diff --git a/Cargo.toml b/Cargo.toml index 6451af51..1034f923 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,6 @@ metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] internals = [] # expose internal data structures debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types -stable_hash = [] # perform all hashing with fixed seed value # compiling for no-std no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng", "hashbrown/ahash-compile-time-rng"] diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..d17bb3da --- /dev/null +++ b/build.rs @@ -0,0 +1,27 @@ +use std::{env, fs::File, io::Write}; + +const WRITE_ERROR: &str = "cannot write to `config.rs`"; + +fn main() { + // Tell Cargo that if the given environment variable changes, to rerun this build script. + println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); + + let mut f = File::create("src/config.rs").expect("cannot create `config.rs`"); + + f.write_fmt(format_args!( + "//! Configuration settings for this Rhai build +#![allow(dead_code)] + +" + )) + .expect(WRITE_ERROR); + + let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); + + f.write_fmt(format_args!( + "pub const AHASH_SEED: Option<[u64; 4]> = {seed};\n" + )) + .expect(WRITE_ERROR); + + f.flush().expect("cannot flush `config.rs`"); +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..43d49b6a --- /dev/null +++ b/src/config.rs @@ -0,0 +1,4 @@ +//! Configuration settings for this Rhai build +#![allow(dead_code)] + +pub const AHASH_SEED: Option<[u64; 4]> = None; diff --git a/src/func/hashing.rs b/src/func/hashing.rs index ae0a57bc..53eac8c3 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -1,5 +1,6 @@ //! Module containing utilities to hash functions and function calls. +use crate::config; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -74,8 +75,8 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - if cfg!(feature = "stable_hash") { - ahash::RandomState::with_seeds(42, 999, 123, 0).build_hasher() + if let Some([seed1, seed2, seed3, seed4]) = config::AHASH_SEED { + ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() } else { ahash::AHasher::default() } diff --git a/src/lib.rs b/src/lib.rs index bb26d72b..4976d4b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,7 @@ use std::prelude::v1::*; // Internal modules mod api; mod ast; +mod config; mod engine; mod eval; mod func; @@ -224,7 +225,7 @@ pub mod debugger { /// 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. #[cfg(not(feature = "internals"))] -pub(crate) type Identifier = SmartString; +type Identifier = SmartString; /// 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. @@ -237,7 +238,7 @@ pub use func::Shared; /// Alias to [`RefCell`][std::cell::RefCell] or [`RwLock`][std::sync::RwLock] depending on the `sync` feature flag. pub use func::Locked; -pub(crate) use func::{calc_fn_hash, calc_fn_params_hash, calc_var_hash, combine_hashes}; +use func::{calc_fn_hash, calc_fn_params_hash, calc_var_hash, combine_hashes}; pub use rhai_codegen::*; @@ -429,7 +430,7 @@ type FnArgsVec = smallvec::SmallVec<[T; 5]>; #[cfg(feature = "no_closure")] type FnArgsVec = crate::StaticVec; -pub(crate) type SmartString = smartstring::SmartString; +type SmartString = smartstring::SmartString; // Compiler guards against mutually-exclusive feature flags From b141e8d0e1ac32165e908519bff75def327c0480 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Sep 2022 16:39:19 +0800 Subject: [PATCH 017/116] Do not use stable hashing with all-zero seed. --- CHANGELOG.md | 3 ++- build.rs | 1 - src/config.rs | 1 - src/func/hashing.rs | 6 +++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70fc8550..28fe4604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ New features ### Stable hashing -* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher in order to force stable (i.e. deterministic) hashes for function signatures. +* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries. +* A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. Enhancements ------------ diff --git a/build.rs b/build.rs index d17bb3da..a8fa7496 100644 --- a/build.rs +++ b/build.rs @@ -10,7 +10,6 @@ fn main() { f.write_fmt(format_args!( "//! Configuration settings for this Rhai build -#![allow(dead_code)] " )) diff --git a/src/config.rs b/src/config.rs index 43d49b6a..91e4716a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,3 @@ //! Configuration settings for this Rhai build -#![allow(dead_code)] pub const AHASH_SEED: Option<[u64; 4]> = None; diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 53eac8c3..cee84062 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -76,7 +76,11 @@ impl BuildHasher for StraightHasherBuilder { #[must_use] pub fn get_hasher() -> ahash::AHasher { if let Some([seed1, seed2, seed3, seed4]) = config::AHASH_SEED { - ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() + if seed1 | seed2 | seed3 | seed4 != 0 { + ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() + } else { + ahash::AHasher::default() + } } else { ahash::AHasher::default() } From fde8483f544cd54a04d8547ee09a5742b18a8217 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Sep 2022 23:04:22 +0800 Subject: [PATCH 018/116] Optimize functions calling. --- src/ast/ast.rs | 19 +++++++++--------- src/ast/expr.rs | 8 ++++---- src/ast/flags.rs | 4 ++-- src/ast/namespace.rs | 4 ++-- src/eval/chaining.rs | 43 +++++++++++++++++++-------------------- src/eval/expr.rs | 10 ++++----- src/eval/stmt.rs | 48 +++++++++++++++++++------------------------- 7 files changed, 64 insertions(+), 72 deletions(-) diff --git a/src/ast/ast.rs b/src/ast/ast.rs index f6ebb50c..eb606d3f 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -69,7 +69,7 @@ impl fmt::Debug for AST { impl AST { /// Create a new [`AST`]. #[cfg(not(feature = "internals"))] - #[inline(always)] + #[inline] #[must_use] pub(crate) fn new( statements: impl IntoIterator, @@ -89,7 +89,7 @@ impl AST { /// _(internals)_ Create a new [`AST`]. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] - #[inline(always)] + #[inline] #[must_use] pub fn new( statements: impl IntoIterator, @@ -108,7 +108,7 @@ impl AST { } /// Create a new [`AST`] with a source name. #[cfg(not(feature = "internals"))] - #[inline(always)] + #[inline] #[must_use] pub(crate) fn new_with_source( statements: impl IntoIterator, @@ -126,7 +126,7 @@ impl AST { /// _(internals)_ Create a new [`AST`] with a source name. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] - #[inline(always)] + #[inline] #[must_use] pub fn new_with_source( statements: impl IntoIterator, @@ -157,7 +157,7 @@ impl AST { } } /// Get the source, if any. - #[inline(always)] + #[inline] #[must_use] pub fn source(&self) -> Option<&str> { if self.source.is_empty() { @@ -664,7 +664,6 @@ impl AST { self.combine_filtered_impl(other, filter) } /// Combine one [`AST`] with another. The second [`AST`] is consumed. - #[inline] fn combine_filtered_impl( &mut self, other: Self, @@ -957,19 +956,21 @@ pub enum ASTNode<'a> { } impl<'a> From<&'a Stmt> for ASTNode<'a> { + #[inline(always)] fn from(stmt: &'a Stmt) -> Self { Self::Stmt(stmt) } } impl<'a> From<&'a Expr> for ASTNode<'a> { + #[inline(always)] fn from(expr: &'a Expr) -> Self { Self::Expr(expr) } } impl PartialEq for ASTNode<'_> { - #[inline(always)] + #[inline] fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Stmt(x), Self::Stmt(y)) => ptr::eq(*x, *y), @@ -986,8 +987,8 @@ impl ASTNode<'_> { #[must_use] pub fn position(&self) -> Position { match self { - ASTNode::Stmt(stmt) => stmt.position(), - ASTNode::Expr(expr) => expr.position(), + Self::Stmt(stmt) => stmt.position(), + Self::Expr(expr) => expr.position(), } } } diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 037079e8..b7f4a836 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -134,7 +134,7 @@ impl fmt::Debug for FnCallHashes { } impl From for FnCallHashes { - #[inline(always)] + #[inline] fn from(hash: u64) -> Self { let hash = if hash == 0 { ALT_ZERO_HASH } else { hash }; @@ -148,7 +148,7 @@ impl From for FnCallHashes { impl FnCallHashes { /// Create a [`FnCallHashes`] with only the native Rust hash. - #[inline(always)] + #[inline] #[must_use] pub const fn from_native(hash: u64) -> Self { Self { @@ -158,7 +158,7 @@ impl FnCallHashes { } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. - #[inline(always)] + #[inline] #[must_use] pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { @@ -252,7 +252,7 @@ pub struct FloatWrapper(F); #[cfg(not(feature = "no_float"))] impl Hash for FloatWrapper { - #[inline(always)] + #[inline] fn hash(&self, state: &mut H) { self.0.to_ne_bytes().hash(state); } diff --git a/src/ast/flags.rs b/src/ast/flags.rs index 452a57e6..e26fb55c 100644 --- a/src/ast/flags.rs +++ b/src/ast/flags.rs @@ -18,7 +18,7 @@ pub enum FnAccess { impl FnAccess { /// Is this function private? - #[inline(always)] + #[inline] #[must_use] pub const fn is_private(self) -> bool { match self { @@ -27,7 +27,7 @@ impl FnAccess { } } /// Is this function public? - #[inline(always)] + #[inline] #[must_use] pub const fn is_public(self) -> bool { match self { diff --git a/src/ast/namespace.rs b/src/ast/namespace.rs index ee36d3bd..88ac8600 100644 --- a/src/ast/namespace.rs +++ b/src/ast/namespace.rs @@ -87,7 +87,7 @@ impl DerefMut for Namespace { } impl From> for Namespace { - #[inline(always)] + #[inline] fn from(mut path: Vec) -> Self { path.shrink_to_fit(); Self { @@ -98,7 +98,7 @@ impl From> for Namespace { } impl From> for Namespace { - #[inline(always)] + #[inline] fn from(mut path: StaticVec) -> Self { path.shrink_to_fit(); Self { index: None, path } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index abdd0573..2b9d6425 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -50,7 +50,7 @@ impl Engine { idx_values: &mut FnArgsVec, chain_type: ChainType, level: usize, - new_val: Option<(Dynamic, &OpAssignment)>, + new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); @@ -75,7 +75,7 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; - let idx_val = idx_values.pop().unwrap(); + let idx_val = &mut idx_values.pop().unwrap(); let mut idx_val_for_setter = idx_val.clone(); let idx_pos = x.lhs.start_position(); let rhs_chain = rhs.into(); @@ -119,12 +119,12 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; - let (new_val, op_info) = new_val.expect("`Some`"); - let idx_val = idx_values.pop().unwrap(); - let mut idx_val2 = idx_val.clone(); + let (new_val, op_info) = new_val.take().expect("`Some`"); + let idx_val = &mut idx_values.pop().unwrap(); + let idx = &mut idx_val.clone(); let try_setter = match self.get_indexed_mut( - global, caches, lib, target, idx_val, pos, true, false, level, + global, caches, lib, target, idx, pos, true, false, level, ) { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { @@ -143,11 +143,10 @@ impl Engine { }; if let Some(mut new_val) = try_setter { - let idx = &mut idx_val2; - // Is this an op-assignment? if op_info.is_op_assignment() { - let idx = &mut idx.clone(); + let idx = &mut idx_val.clone(); + // Call the index getter to get the current value if let Ok(val) = self.call_indexer_get(global, caches, lib, target, idx, level) @@ -167,8 +166,9 @@ impl Engine { // Try to call index setter let new_val = &mut new_val; + self.call_indexer_set( - global, caches, lib, target, idx, new_val, is_ref_mut, level, + global, caches, lib, target, idx_val, new_val, is_ref_mut, level, )?; } @@ -179,7 +179,7 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; - let idx_val = idx_values.pop().unwrap(); + let idx_val = &mut idx_values.pop().unwrap(); self.get_indexed_mut( global, caches, lib, target, idx_val, pos, false, true, level, @@ -236,8 +236,8 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; - let index = x.2.clone().into(); - let (new_val, op_info) = new_val.expect("`Some`"); + let index = &mut x.2.clone().into(); + let (new_val, op_info) = new_val.take().expect("`Some`"); { let val_target = &mut self.get_indexed_mut( global, caches, lib, target, index, *pos, true, false, level, @@ -255,7 +255,7 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; - let index = x.2.clone().into(); + let index = &mut x.2.clone().into(); let val = self.get_indexed_mut( global, caches, lib, target, index, *pos, false, false, level, )?; @@ -267,7 +267,7 @@ impl Engine { self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; let ((getter, hash_get), (setter, hash_set), name) = &**x; - let (mut new_val, op_info) = new_val.expect("`Some`"); + let (mut new_val, op_info) = new_val.take().expect("`Some`"); if op_info.is_op_assignment() { let args = &mut [target.as_mut()]; @@ -368,7 +368,7 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, _node, level)?; - let index = p.2.clone().into(); + let index = &mut p.2.clone().into(); self.get_indexed_mut( global, caches, lib, target, index, pos, false, true, level, )? @@ -558,7 +558,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, level: usize, - new_val: Option<(Dynamic, &OpAssignment)>, + new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { let chain_type = ChainType::from(expr); let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr { @@ -595,7 +595,7 @@ impl Engine { // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( - scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0, + scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, level, )?; } @@ -653,7 +653,6 @@ impl Engine { parent_options: ASTFlags, _parent_chain_type: ChainType, idx_values: &mut FnArgsVec, - size: usize, level: usize, ) -> RhaiResultOf<()> { #[cfg(not(feature = "unchecked"))] @@ -731,7 +730,7 @@ impl Engine { self.eval_dot_index_chain_arguments( scope, global, caches, lib, this_ptr, rhs, *options, chain_type, idx_values, - size, level, + level, )?; if !_arg_values.is_empty() { @@ -809,7 +808,7 @@ impl Engine { caches: &mut Caches, lib: &[&Module], target: &'t mut Dynamic, - mut idx: Dynamic, + idx: &mut Dynamic, idx_pos: Position, _add_if_not_found: bool, use_indexers: bool, @@ -1018,7 +1017,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, caches, lib, target, &mut idx, level) + .call_indexer_get(global, caches, lib, target, idx, level) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/expr.rs b/src/eval/expr.rs index cdbeb95e..c965ddda 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -561,14 +561,12 @@ impl Engine { } #[cfg(not(feature = "no_index"))] - Expr::Index(..) => { - self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None) - } + Expr::Index(..) => self + .eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, &mut None), #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => { - self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None) - } + Expr::Dot(..) => self + .eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, &mut None), _ => unreachable!("expression cannot be evaluated: {:?}", expr), }; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 58f7ce47..5fa46785 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -187,15 +187,6 @@ impl Engine { *target.write_lock::().unwrap() = new_val; } - /* - if let Some(mut guard) = target.write_lock::() { - if guard.is::() { - let s = std::mem::take(&mut *guard).cast::(); - *guard = self.get_interned_string(s).into(); - } - } - */ - target.propagate_changed_value(op_info.pos) } @@ -295,19 +286,25 @@ impl Engine { } else { let (op_info, BinaryExpr { lhs, rhs }) = &**x; - let rhs_result = self - .eval_expr(scope, global, caches, lib, this_ptr, rhs, level) - .map(Dynamic::flatten); + let rhs_result = self.eval_expr(scope, global, caches, lib, this_ptr, rhs, level); if let Ok(rhs_val) = rhs_result { - let rhs_val = if rhs_val.is::() { - self.get_interned_string(rhs_val.cast::()) - .into() + // Check if the result is a string. If so, intern it. + #[cfg(not(feature = "no_closure"))] + let is_string = !rhs_val.is_shared() && rhs_val.is::(); + #[cfg(feature = "no_closure")] + let is_string = rhs_val.is::(); + + let rhs_val = if is_string { + self.get_interned_string( + rhs_val.into_immutable_string().expect("`ImmutableString`"), + ) + .into() } else { - rhs_val + rhs_val.flatten() }; - let _new_val = Some((rhs_val, op_info)); + let _new_val = &mut Some((rhs_val, op_info)); // Must be either `var[index] op= val` or `var.prop op= val` match lhs { @@ -317,20 +314,17 @@ impl Engine { } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain( - scope, global, caches, lib, this_ptr, lhs, level, _new_val, - ) - .map(|_| Dynamic::UNIT), + Expr::Index(..) => self.eval_dot_index_chain( + scope, global, caches, lib, this_ptr, lhs, level, _new_val, + ), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain( - scope, global, caches, lib, this_ptr, lhs, level, _new_val, - ) - .map(|_| Dynamic::UNIT), + Expr::Dot(..) => self.eval_dot_index_chain( + scope, global, caches, lib, this_ptr, lhs, level, _new_val, + ), _ => unreachable!("cannot assign to expression: {:?}", lhs), } + .map(|_| Dynamic::UNIT) } else { rhs_result } From 82b64e9c7a68469dd35f2ebe9edc2e47f70b7373 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Sep 2022 23:08:27 +0800 Subject: [PATCH 019/116] Fix builds. --- src/eval/stmt.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 5fa46785..018a6bf4 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -314,17 +314,20 @@ impl Engine { } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self.eval_dot_index_chain( - scope, global, caches, lib, this_ptr, lhs, level, _new_val, - ), + Expr::Index(..) => self + .eval_dot_index_chain( + scope, global, caches, lib, this_ptr, lhs, level, _new_val, + ) + .map(|_| Dynamic::UNIT), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self.eval_dot_index_chain( - scope, global, caches, lib, this_ptr, lhs, level, _new_val, - ), + Expr::Dot(..) => self + .eval_dot_index_chain( + scope, global, caches, lib, this_ptr, lhs, level, _new_val, + ) + .map(|_| Dynamic::UNIT), _ => unreachable!("cannot assign to expression: {:?}", lhs), } - .map(|_| Dynamic::UNIT) } else { rhs_result } From 6c777e68d3ac1dfd986c8eafd9fd714628cad253 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 28 Sep 2022 12:06:22 +0800 Subject: [PATCH 020/116] Refine inlining. --- examples/custom_types.rs | 2 ++ src/api/custom_syntax.rs | 1 - src/api/definitions/mod.rs | 1 + src/api/limits.rs | 1 + src/ast/ast.rs | 1 + src/ast/expr.rs | 5 ++--- src/ast/ident.rs | 2 -- src/ast/namespace.rs | 2 -- src/ast/stmt.rs | 4 ++-- src/engine.rs | 1 + src/eval/debugger.rs | 1 + src/eval/global_state.rs | 5 +---- src/eval/target.rs | 2 -- src/func/func.rs | 2 ++ src/func/hashing.rs | 2 ++ src/func/register.rs | 7 ++++--- src/func/script.rs | 9 +++------ src/module/mod.rs | 1 + src/module/resolvers/collection.rs | 3 ++- src/module/resolvers/file.rs | 1 + src/module/resolvers/stat.rs | 1 + src/optimizer.rs | 1 + src/packages/mod.rs | 18 ++++++++++++++++-- src/parser.rs | 16 ++++------------ src/tokenizer.rs | 9 ++++++--- src/types/dynamic.rs | 2 -- src/types/immutable_string.rs | 7 ++++++- src/types/interner.rs | 1 + src/types/scope.rs | 5 ++--- tests/build_type.rs | 2 ++ tests/for.rs | 2 ++ 31 files changed, 68 insertions(+), 49 deletions(-) diff --git a/examples/custom_types.rs b/examples/custom_types.rs index 3fa7c7ef..ba57de58 100644 --- a/examples/custom_types.rs +++ b/examples/custom_types.rs @@ -36,6 +36,8 @@ fn main() -> Result<(), Box> { type Item = i64; type IntoIter = std::vec::IntoIter; + #[inline] + #[must_use] fn into_iter(self) -> Self::IntoIter { vec![self.x - 1, self.x, self.x + 1].into_iter() } diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 080d4477..7ee568c0 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -157,7 +157,6 @@ impl Deref for Expression<'_> { type Target = Expr; #[inline(always)] - #[must_use] fn deref(&self) -> &Self::Target { self.0 } diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 73908d04..cd0a51c2 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -77,6 +77,7 @@ pub struct DefinitionsConfig { impl Default for DefinitionsConfig { #[inline(always)] + #[must_use] fn default() -> Self { Self { write_headers: false, diff --git a/src/api/limits.rs b/src/api/limits.rs index 18eb17f1..9c70c132 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -75,6 +75,7 @@ impl Limits { impl Default for Limits { #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } diff --git a/src/ast/ast.rs b/src/ast/ast.rs index eb606d3f..d0b88e26 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -36,6 +36,7 @@ pub struct AST { impl Default for AST { #[inline(always)] + #[must_use] fn default() -> Self { Self::empty() } diff --git a/src/ast/expr.rs b/src/ast/expr.rs index b7f4a836..76c9587d 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -75,8 +75,8 @@ impl CustomExpr { /// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)? /// /// A self-terminated custom syntax always ends in `$block$`, `}` or `;` - #[must_use] #[inline(always)] + #[must_use] pub const fn is_self_terminated(&self) -> bool { self.self_terminated } @@ -281,7 +281,6 @@ impl Deref for FloatWrapper { type Target = F; #[inline(always)] - #[must_use] fn deref(&self) -> &Self::Target { &self.0 } @@ -290,7 +289,6 @@ impl Deref for FloatWrapper { #[cfg(not(feature = "no_float"))] impl DerefMut for FloatWrapper { #[inline(always)] - #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -447,6 +445,7 @@ pub enum Expr { impl Default for Expr { #[inline(always)] + #[must_use] fn default() -> Self { Self::Unit(Position::NONE) } diff --git a/src/ast/ident.rs b/src/ast/ident.rs index eb84c432..e8eb6626 100644 --- a/src/ast/ident.rs +++ b/src/ast/ident.rs @@ -40,7 +40,6 @@ impl Deref for Ident { type Target = ImmutableString; #[inline(always)] - #[must_use] fn deref(&self) -> &Self::Target { &self.name } @@ -48,7 +47,6 @@ impl Deref for Ident { impl DerefMut for Ident { #[inline(always)] - #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.name } diff --git a/src/ast/namespace.rs b/src/ast/namespace.rs index 88ac8600..e0939384 100644 --- a/src/ast/namespace.rs +++ b/src/ast/namespace.rs @@ -72,7 +72,6 @@ impl Deref for Namespace { type Target = StaticVec; #[inline(always)] - #[must_use] fn deref(&self) -> &Self::Target { &self.path } @@ -80,7 +79,6 @@ impl Deref for Namespace { impl DerefMut for Namespace { #[inline(always)] - #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.path } diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index ba6f0821..a763860c 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -209,6 +209,7 @@ impl IntoIterator for RangeCase { type IntoIter = Box>; #[inline(always)] + #[must_use] fn into_iter(self) -> Self::IntoIter { match self { Self::ExclusiveInt(r, ..) => Box::new(r), @@ -426,7 +427,6 @@ impl Deref for StmtBlock { type Target = StmtBlockContainer; #[inline(always)] - #[must_use] fn deref(&self) -> &Self::Target { &self.block } @@ -434,7 +434,6 @@ impl Deref for StmtBlock { impl DerefMut for StmtBlock { #[inline(always)] - #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.block } @@ -607,6 +606,7 @@ pub enum Stmt { impl Default for Stmt { #[inline(always)] + #[must_use] fn default() -> Self { Self::Noop(Position::NONE) } diff --git a/src/engine.rs b/src/engine.rs index fb905cde..cb4cb094 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -190,6 +190,7 @@ impl fmt::Debug for Engine { impl Default for Engine { #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index e98771e7..d6a33624 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -48,6 +48,7 @@ pub enum DebuggerCommand { impl Default for DebuggerCommand { #[inline(always)] + #[must_use] fn default() -> Self { Self::Continue } diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 20ee2614..3d271af2 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -316,7 +316,6 @@ impl IntoIterator for GlobalRuntimeState<'_> { std::iter::Rev; 3]>>, >; - #[inline] fn into_iter(self) -> Self::IntoIter { self.keys .into_iter() @@ -333,10 +332,8 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { std::iter::Rev>>, >; - #[inline] fn into_iter(self) -> Self::IntoIter { - let x = self.keys.iter().rev().zip(self.modules.iter().rev()); - x + self.keys.iter().rev().zip(self.modules.iter().rev()) } } diff --git a/src/eval/target.rs b/src/eval/target.rs index 3b47533d..5a441520 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -399,7 +399,6 @@ impl Deref for Target<'_> { type Target = Dynamic; #[inline] - #[must_use] fn deref(&self) -> &Dynamic { match self { Self::RefMut(r) => r, @@ -425,7 +424,6 @@ impl AsRef for Target<'_> { impl DerefMut for Target<'_> { #[inline] - #[must_use] fn deref_mut(&mut self) -> &mut Dynamic { match self { Self::RefMut(r) => r, diff --git a/src/func/func.rs b/src/func/func.rs index f74873c1..7d211879 100644 --- a/src/func/func.rs +++ b/src/func/func.rs @@ -46,6 +46,7 @@ pub trait Func { /// func(123, "hello")? == false; // call the anonymous function /// # Ok(()) /// # } + #[must_use] fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output; /// Create a Rust closure from a script. @@ -79,6 +80,7 @@ pub trait Func { /// # Ok(()) /// # } /// ``` + #[must_use] fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult; } diff --git a/src/func/hashing.rs b/src/func/hashing.rs index cee84062..de261de7 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -41,6 +41,7 @@ pub struct StraightHasher(u64); impl Hasher for StraightHasher { #[inline(always)] + #[must_use] fn finish(&self) -> u64 { self.0 } @@ -66,6 +67,7 @@ impl BuildHasher for StraightHasherBuilder { type Hasher = StraightHasher; #[inline(always)] + #[must_use] fn build_hasher(&self) -> Self::Hasher { StraightHasher(ALT_ZERO_HASH) } diff --git a/src/func/register.rs b/src/func/register.rs index 37cc6bd0..928b08b0 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -86,8 +86,11 @@ pub trait RegisterNativeFunction { /// _(metadata)_ Get the type name of this function's return value. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] + #[inline(always)] #[must_use] - fn return_type_name() -> &'static str; + fn return_type_name() -> &'static str { + std::any::type_name::() + } } const EXPECT_ARGS: &str = "arguments"; @@ -138,7 +141,6 @@ macro_rules! def_register { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } - #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! @@ -164,7 +166,6 @@ macro_rules! def_register { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } - #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! diff --git a/src/func/script.rs b/src/func/script.rs index 8ccc876b..8d8139d6 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -251,12 +251,9 @@ impl Engine { // Then check sub-modules || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); - if !result { - if cache.filter.is_absent(hash_script) { - cache.filter.mark(hash_script); - } else { - cache.map.insert(hash_script, None); - } + if !result && !cache.filter.is_absent_and_set(hash_script) { + // Do not cache "one-hit wonders" + cache.map.insert(hash_script, None); } result diff --git a/src/module/mod.rs b/src/module/mod.rs index fb94e590..0479ee70 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -196,6 +196,7 @@ pub struct Module { impl Default for Module { #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 7aab4bd0..fd6780d4 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -42,7 +42,7 @@ impl ModuleResolversCollection { /// ``` #[inline(always)] #[must_use] - pub fn new() -> Self { + pub const fn new() -> Self { Self(Vec::new()) } /// Append a [module resolver][ModuleResolver] to the end. @@ -112,6 +112,7 @@ impl IntoIterator for ModuleResolversCollection { type IntoIter = IntoIter>; #[inline(always)] + #[must_use] fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 729e772f..07718c6c 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -56,6 +56,7 @@ pub struct FileModuleResolver { impl Default for FileModuleResolver { #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 4019fc6d..8338a43a 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -134,6 +134,7 @@ impl IntoIterator for StaticModuleResolver { type IntoIter = IntoIter>; #[inline(always)] + #[must_use] fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } diff --git a/src/optimizer.rs b/src/optimizer.rs index c46cf43f..79e1c130 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -38,6 +38,7 @@ pub enum OptimizationLevel { impl Default for OptimizationLevel { #[inline(always)] + #[must_use] fn default() -> Self { Self::Simple } diff --git a/src/packages/mod.rs b/src/packages/mod.rs index ab4df033..5a63e86b 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -45,11 +45,14 @@ pub use time_basic::BasicTimePackage; pub trait Package { /// Initialize the package. /// Functions should be registered into `module` here. + #[cold] fn init(module: &mut Module); /// Initialize the package with an [`Engine`]. /// /// Perform tasks such as registering custom operators/syntax. + #[cold] + #[inline] #[allow(unused_variables)] fn init_engine(engine: &mut Engine) {} @@ -65,6 +68,8 @@ pub trait Package { /// /// package.register_into_engine(&mut engine); /// ``` + #[cold] + #[inline] fn register_into_engine(&self, engine: &mut Engine) -> &Self { Self::init_engine(engine); engine.register_global_module(self.as_shared_module()); @@ -84,6 +89,8 @@ pub trait Package { /// package.register_into_engine_as(&mut engine, "core"); /// ``` #[cfg(not(feature = "no_module"))] + #[cold] + #[inline] fn register_into_engine_as(&self, engine: &mut Engine, name: &str) -> &Self { Self::init_engine(engine); engine.register_static_module(name, self.as_shared_module()); @@ -133,7 +140,6 @@ macro_rules! def_package { fn as_shared_module(&self) -> $crate::Shared<$crate::Module> { self.0.clone() } - #[inline] fn init($lib: &mut $crate::Module) { $($( $(#[$base_meta])* { <$base_pkg>::init($lib); } @@ -141,7 +147,6 @@ macro_rules! def_package { $block } - #[inline] fn init_engine(_engine: &mut $crate::Engine) { $($( $(#[$base_meta])* { <$base_pkg>::init_engine(_engine); } @@ -156,6 +161,7 @@ macro_rules! def_package { impl Default for $package { #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } @@ -193,12 +199,16 @@ macro_rules! def_package { } impl Default for $package { + #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } } impl $package { + #[inline] + #[must_use] pub fn new() -> Self { let mut module = $root::Module::new(); ::init(&mut module); @@ -229,12 +239,16 @@ macro_rules! def_package { } impl Default for $package { + #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } } impl $package { + #[inline] + #[must_use] pub fn new() -> Self { let mut module = $root::Module::new(); ::init(&mut module); diff --git a/src/parser.rs b/src/parser.rs index 6cdf3d18..d10f132c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -110,7 +110,7 @@ impl fmt::Debug for ParseState<'_> { impl<'e> ParseState<'e> { /// Create a new [`ParseState`]. - #[inline(always)] + #[inline] #[must_use] pub fn new( engine: &Engine, @@ -183,7 +183,6 @@ impl<'e> ParseState<'e> { /// /// * `is_func_name`: `true` if the variable is actually the name of a function /// (in which case it will be converted into a function pointer). - #[inline] #[must_use] pub fn access_var( &mut self, @@ -235,7 +234,6 @@ impl<'e> ParseState<'e> { /// /// Panics when called under `no_module`. #[cfg(not(feature = "no_module"))] - #[inline] #[must_use] pub fn find_module(&self, name: &str) -> Option { self.imports @@ -258,7 +256,7 @@ impl<'e> ParseState<'e> { /// Get an interned property getter, creating one if it is not yet interned. #[cfg(not(feature = "no_object"))] - #[inline(always)] + #[inline] #[must_use] pub fn get_interned_getter( &mut self, @@ -273,7 +271,7 @@ impl<'e> ParseState<'e> { /// Get an interned property setter, creating one if it is not yet interned. #[cfg(not(feature = "no_object"))] - #[inline(always)] + #[inline] #[must_use] pub fn get_interned_setter( &mut self, @@ -313,7 +311,7 @@ struct ParseSettings { impl ParseSettings { /// Create a new `ParseSettings` with one higher expression level. - #[inline(always)] + #[inline] #[must_use] pub const fn level_up(&self) -> Self { Self { @@ -417,7 +415,6 @@ impl Expr { } /// Make sure that the next expression is not a statement expression (i.e. wrapped in `{}`). -#[inline] fn ensure_not_statement_expr( input: &mut TokenStream, type_name: &(impl ToString + ?Sized), @@ -429,7 +426,6 @@ fn ensure_not_statement_expr( } /// Make sure that the next expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). -#[inline] fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> { match input.peek().expect(NEVER_ENDS) { (Token::Equals, pos) => Err(LexError::ImproperSymbol( @@ -446,7 +442,6 @@ fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> { /// # Panics /// /// Panics if the next token is not the expected one. -#[inline] fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position { let (t, pos) = input.next().expect(NEVER_ENDS); @@ -462,7 +457,6 @@ fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position { } /// Match a particular [token][Token], consuming it if matched. -#[inline] fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { let (t, pos) = input.peek().expect(NEVER_ENDS); if *t == token { @@ -473,7 +467,6 @@ fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { } /// Parse a variable name. -#[inline] fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> { match input.next().expect(NEVER_ENDS) { // Variable name @@ -491,7 +484,6 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position /// Parse a symbol. #[cfg(not(feature = "no_custom_syntax"))] -#[inline] fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> { match input.next().expect(NEVER_ENDS) { // Symbol diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 81893066..6aa0ec43 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -32,7 +32,7 @@ pub struct TokenizerControlBlock { impl TokenizerControlBlock { /// Create a new `TokenizerControlBlock`. - #[inline(always)] + #[inline] #[must_use] pub const fn new() -> Self { Self { @@ -97,7 +97,7 @@ impl Position { /// # Panics /// /// Panics if `line` is zero. - #[inline(always)] + #[inline] #[must_use] pub const fn new(line: u16, position: u16) -> Self { assert!(line != 0, "line cannot be zero"); @@ -220,6 +220,7 @@ impl Position { impl Default for Position { #[inline(always)] + #[must_use] fn default() -> Self { Self::START } @@ -299,6 +300,8 @@ pub struct Span { } impl Default for Span { + #[inline(always)] + #[must_use] fn default() -> Self { Self::NONE } @@ -315,7 +318,7 @@ impl Span { Self { start, end } } /// Is this [`Span`] non-existent? - #[inline(always)] + #[inline] #[must_use] pub const fn is_none(&self) -> bool { self.start.is_none() && self.end.is_none() diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 79e63abf..6572dbe4 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -153,7 +153,6 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { type Target = T; #[inline] - #[must_use] fn deref(&self) -> &Self::Target { match self.0 { DynamicWriteLockInner::Reference(ref reference) => *reference, @@ -165,7 +164,6 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { #[inline] - #[must_use] fn deref_mut(&mut self) -> &mut Self::Target { match self.0 { DynamicWriteLockInner::Reference(ref mut reference) => *reference, diff --git a/src/types/immutable_string.rs b/src/types/immutable_string.rs index 7353f151..f6dba61a 100644 --- a/src/types/immutable_string.rs +++ b/src/types/immutable_string.rs @@ -53,7 +53,6 @@ impl Deref for ImmutableString { type Target = SmartString; #[inline(always)] - #[must_use] fn deref(&self) -> &Self::Target { &self.0 } @@ -148,6 +147,7 @@ impl FromStr for ImmutableString { type Err = (); #[inline(always)] + #[must_use] fn from_str(s: &str) -> Result { let s: SmartString = s.into(); Ok(Self(s.into())) @@ -156,6 +156,7 @@ impl FromStr for ImmutableString { impl FromIterator for ImmutableString { #[inline] + #[must_use] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } @@ -163,6 +164,7 @@ impl FromIterator for ImmutableString { impl<'a> FromIterator<&'a char> for ImmutableString { #[inline] + #[must_use] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().copied().collect::().into()) } @@ -170,6 +172,7 @@ impl<'a> FromIterator<&'a char> for ImmutableString { impl<'a> FromIterator<&'a str> for ImmutableString { #[inline] + #[must_use] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } @@ -177,6 +180,7 @@ impl<'a> FromIterator<&'a str> for ImmutableString { impl FromIterator for ImmutableString { #[inline] + #[must_use] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } @@ -184,6 +188,7 @@ impl FromIterator for ImmutableString { impl FromIterator for ImmutableString { #[inline] + #[must_use] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } diff --git a/src/types/interner.rs b/src/types/interner.rs index 7ae35a35..5832dbe0 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -39,6 +39,7 @@ pub struct StringsInterner<'a> { impl Default for StringsInterner<'_> { #[inline(always)] + #[must_use] fn default() -> Self { Self::new() } diff --git a/src/types/scope.rs b/src/types/scope.rs index 169830b4..a3787421 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -128,7 +128,7 @@ impl IntoIterator for Scope<'_> { type Item = (String, Dynamic, Vec); type IntoIter = Box>; - #[inline] + #[must_use] fn into_iter(self) -> Self::IntoIter { Box::new( self.values @@ -143,7 +143,7 @@ impl<'a> IntoIterator for &'a Scope<'_> { type Item = (&'a Identifier, &'a Dynamic, &'a Vec); type IntoIter = Box + 'a>; - #[inline] + #[must_use] fn into_iter(self) -> Self::IntoIter { Box::new( self.values @@ -719,7 +719,6 @@ impl Scope<'_> { scope } /// Get an iterator to entries in the [`Scope`]. - #[inline] #[allow(dead_code)] pub(crate) fn into_iter(self) -> impl Iterator)> { self.names diff --git a/tests/build_type.rs b/tests/build_type.rs index 05b23bc2..4844a3b4 100644 --- a/tests/build_type.rs +++ b/tests/build_type.rs @@ -49,6 +49,8 @@ fn build_type() -> Result<(), Box> { type Item = INT; type IntoIter = std::vec::IntoIter; + #[inline] + #[must_use] fn into_iter(self) -> Self::IntoIter { vec![self.x, self.y, self.z].into_iter() } diff --git a/tests/for.rs b/tests/for.rs index 62112a12..92e68af1 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -337,6 +337,8 @@ impl IntoIterator for MyIterableType { type Item = char; type IntoIter = std::vec::IntoIter; + #[inline] + #[must_use] fn into_iter(self) -> Self::IntoIter { self.0.chars().collect::>().into_iter() } From e8fd965eba7f16bbb90e17ee9955b30ab040a985 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 29 Sep 2022 22:46:59 +0800 Subject: [PATCH 021/116] Add parse_json. --- CHANGELOG.md | 6 ++++ src/api/compile.rs | 2 +- src/api/eval.rs | 1 + src/api/json.rs | 5 +-- src/packages/lang_core.rs | 18 ++++++++++- src/packages/map_basic.rs | 7 +++-- src/parser.rs | 64 +++++++++++++++++++++++---------------- src/types/error.rs | 4 +-- tests/maps.rs | 5 +++ 9 files changed, 78 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28fe4604..81e4c859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ New features * It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries. * A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. +Bug fixes +--------- + +* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications. + Enhancements ------------ @@ -19,6 +24,7 @@ Enhancements * Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. * `Scope` is now serializable and deserializable via `serde`. * `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline. +* `parse_json` function is added to parse a JSON string into an object map. Version 1.10.1 diff --git a/src/api/compile.rs b/src/api/compile.rs index 5322dbeb..2d187160 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -295,6 +295,6 @@ impl Engine { let mut peekable = stream.peekable(); let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control); - self.parse_global_expr(&mut peekable, &mut state, self.optimization_level) + self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level) } } diff --git a/src/api/eval.rs b/src/api/eval.rs index 4b22d99c..6808e2e6 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -122,6 +122,7 @@ impl Engine { let ast = self.parse_global_expr( &mut stream.peekable(), &mut state, + |_| {}, #[cfg(not(feature = "no_optimize"))] OptimizationLevel::None, #[cfg(feature = "no_optimize")] diff --git a/src/api/json.rs b/src/api/json.rs index 034062bf..24069fdd 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -23,7 +23,7 @@ impl Engine { /// JSON sub-objects are handled transparently. /// /// This function can be used together with [`format_map_as_json`] to work with JSON texts - /// without using the [`serde`](https://crates.io/crates/serde) crate (which is heavy). + /// without using the [`serde_json`](https://crates.io/crates/serde_json) crate (which is heavy). /// /// # Example /// @@ -122,6 +122,7 @@ impl Engine { let ast = self.parse_global_expr( &mut stream.peekable(), &mut state, + |s| s.allow_unquoted_map_properties = false, #[cfg(not(feature = "no_optimize"))] OptimizationLevel::None, #[cfg(feature = "no_optimize")] @@ -137,7 +138,7 @@ impl Engine { /// Not available under `no_std`. /// /// This function can be used together with [`Engine::parse_json`] to work with JSON texts -/// without using the [`serde`](https://crates.io/crates/serde) crate (which is heavy). +/// without using the [`serde_json`](https://crates.io/crates/serde_json) crate (which is heavy). /// /// # Data types /// diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 5eaa5ea3..d423691d 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -88,7 +88,6 @@ mod core_functions { #[cfg(feature = "f32_float")] std::thread::sleep(std::time::Duration::from_secs_f32(seconds)); } - /// Block the current thread for a particular number of `seconds`. #[cfg(not(feature = "no_std"))] pub fn sleep(seconds: INT) { @@ -97,6 +96,23 @@ mod core_functions { } std::thread::sleep(std::time::Duration::from_secs(seconds as u64)); } + + /// Parse a JSON string into a value. + /// + /// # Example + /// + /// ```rhai + /// let m = parse_json(`{"a":1, "b":2, "c":3}`); + /// + /// print(m); // prints #{"a":1, "b":2, "c":3} + /// ``` + #[cfg(not(feature = "no_index"))] + #[cfg(not(feature = "no_object"))] + #[cfg(feature = "metadata")] + #[rhai_fn(return_raw)] + pub fn parse_json(_ctx: NativeCallContext, json: &str) -> RhaiResultOf { + serde_json::from_str(json).map_err(|err| err.to_string().into()) + } } #[cfg(not(feature = "no_function"))] diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 955b8395..2d25641d 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -2,7 +2,7 @@ use crate::engine::OP_EQUALS; use crate::plugin::*; -use crate::{def_package, format_map_as_json, Dynamic, ImmutableString, Map, RhaiResultOf, INT}; +use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -304,6 +304,9 @@ mod map_functions { /// print(m.to_json()); // prints {"a":1, "b":2, "c":3} /// ``` pub fn to_json(map: &mut Map) -> String { - format_map_as_json(map) + #[cfg(feature = "metadata")] + return serde_json::to_string(map).unwrap_or_else(|_| "ERROR".into()); + #[cfg(not(feature = "metadata"))] + return crate::format_map_as_json(map); } } diff --git a/src/parser.rs b/src/parser.rs index d10f132c..0ec27614 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -287,26 +287,28 @@ impl<'e> ParseState<'e> { /// A type that encapsulates all the settings for a particular parsing function. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -struct ParseSettings { +pub(crate) struct ParseSettings { /// Is the construct being parsed located at global level? - at_global_level: bool, + pub at_global_level: bool, /// Is the construct being parsed located inside a function definition? #[cfg(not(feature = "no_function"))] - in_fn_scope: bool, + pub in_fn_scope: bool, /// Is the construct being parsed located inside a closure definition? #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_closure"))] - in_closure: bool, + pub in_closure: bool, /// Is the construct being parsed located inside a breakable loop? - is_breakable: bool, + pub is_breakable: bool, /// Allow statements in blocks? - allow_statements: bool, + pub allow_statements: bool, + /// Allow unquoted map properties? + pub allow_unquoted_map_properties: bool, /// Language options in effect (overrides Engine options). - options: LangOptions, + pub options: LangOptions, /// Current expression nesting level. - level: usize, + pub level: usize, /// Current position. - pos: Position, + pub pos: Position, } impl ParseSettings { @@ -996,6 +998,9 @@ impl Engine { } let (name, pos) = match input.next().expect(NEVER_ENDS) { + (Token::Identifier(..), pos) if !settings.allow_unquoted_map_properties => { + return Err(PERR::PropertyExpected.into_err(pos)) + } (Token::Identifier(s) | Token::StringConstant(s), pos) => { if map.iter().any(|(p, ..)| **p == *s) { return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); @@ -3326,6 +3331,7 @@ impl Engine { in_closure: false, is_breakable: false, allow_statements: true, + allow_unquoted_map_properties: settings.allow_unquoted_map_properties, level: 0, options, pos, @@ -3793,6 +3799,7 @@ impl Engine { &self, input: &mut TokenStream, state: &mut ParseState, + process_settings: impl Fn(&mut ParseSettings), _optimization_level: OptimizationLevel, ) -> ParseResult { let mut functions = StraightHashMap::default(); @@ -3802,7 +3809,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] options.remove(LangOptions::ANON_FN); - let settings = ParseSettings { + let mut settings = ParseSettings { at_global_level: true, #[cfg(not(feature = "no_function"))] in_fn_scope: false, @@ -3811,10 +3818,13 @@ impl Engine { in_closure: false, is_breakable: false, allow_statements: false, + allow_unquoted_map_properties: true, level: 0, options, - pos: Position::NONE, + pos: Position::START, }; + process_settings(&mut settings); + let expr = self.parse_expr(input, state, &mut functions, settings)?; assert!(functions.is_empty()); @@ -3853,25 +3863,27 @@ impl Engine { &self, input: &mut TokenStream, state: &mut ParseState, + process_settings: impl Fn(&mut ParseSettings), ) -> ParseResult<(StmtBlockContainer, StaticVec>)> { let mut statements = StmtBlockContainer::new_const(); let mut functions = StraightHashMap::default(); + let mut settings = ParseSettings { + at_global_level: true, + #[cfg(not(feature = "no_function"))] + in_fn_scope: false, + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_closure"))] + in_closure: false, + is_breakable: false, + allow_statements: true, + allow_unquoted_map_properties: true, + options: self.options, + level: 0, + pos: Position::START, + }; + process_settings(&mut settings); while !input.peek().expect(NEVER_ENDS).0.is_eof() { - let settings = ParseSettings { - at_global_level: true, - #[cfg(not(feature = "no_function"))] - in_fn_scope: false, - #[cfg(not(feature = "no_function"))] - #[cfg(not(feature = "no_closure"))] - in_closure: false, - is_breakable: false, - allow_statements: true, - options: self.options, - level: 0, - pos: Position::NONE, - }; - let stmt = self.parse_stmt(input, state, &mut functions, settings)?; if stmt.is_noop() { @@ -3918,7 +3930,7 @@ impl Engine { state: &mut ParseState, _optimization_level: OptimizationLevel, ) -> ParseResult { - let (statements, _lib) = self.parse_global_level(input, state)?; + let (statements, _lib) = self.parse_global_level(input, state, |_| {})?; #[cfg(not(feature = "no_optimize"))] return Ok(crate::optimizer::optimize_into_ast( diff --git a/src/types/error.rs b/src/types/error.rs index ba9b7351..89bbedd8 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -251,9 +251,9 @@ impl> From for EvalAltResult { impl> From for Box { #[cold] - #[inline(never)] + #[inline(always)] fn from(err: T) -> Self { - EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into() + Into::::into(err).into() } } diff --git a/tests/maps.rs b/tests/maps.rs index 3cb414db..2620738d 100644 --- a/tests/maps.rs +++ b/tests/maps.rs @@ -276,6 +276,11 @@ fn test_map_json() -> Result<(), Box> { EvalAltResult::ErrorMismatchOutputType(..) )); + assert!(matches!( + *engine.parse_json("{a:42}", true).expect_err("should error"), + EvalAltResult::ErrorParsing(..) + )); + assert!(matches!( *engine .parse_json("#{a:123}", true) From 891214470b357e3af531458fa9af19c79e10f50a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Oct 2022 15:33:51 +0800 Subject: [PATCH 022/116] Make limit getters available without unchecked. --- CHANGELOG.md | 1 + src/api/limits.rs | 50 +++++++++++++++++++---------------- src/api/mod.rs | 52 +++++++++++++++++++++++++++++++++++++ src/api/register.rs | 4 +-- src/eval/global_state.rs | 1 + src/eval/stmt.rs | 3 +-- src/func/script.rs | 1 - src/module/mod.rs | 4 +-- src/packages/blob_basic.rs | 2 -- src/packages/string_more.rs | 4 --- src/parser.rs | 2 -- 11 files changed, 87 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e4c859..c1fe04c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Enhancements * `Scope` is now serializable and deserializable via `serde`. * `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline. * `parse_json` function is added to parse a JSON string into an object map. +* Methods return maximum limits (e.g. `max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code. Version 1.10.1 diff --git a/src/api/limits.rs b/src/api/limits.rs index 9c70c132..70de6592 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -94,12 +94,14 @@ impl Engine { } /// The maximum levels of function calls allowed for a script. /// - /// Not available under `unchecked` or `no_function`. - #[cfg(not(feature = "no_function"))] + /// Zero under `no_function`. #[inline(always)] #[must_use] pub const fn max_call_levels(&self) -> usize { - self.limits.max_call_stack_depth + #[cfg(not(feature = "no_function"))] + return self.limits.max_call_stack_depth; + #[cfg(feature = "no_function")] + return 0; } /// Set the maximum number of operations allowed for a script to run to avoid /// consuming too much resources (0 for unlimited). @@ -133,12 +135,14 @@ impl Engine { } /// The maximum number of imported [modules][crate::Module] allowed for a script. /// - /// Not available under `unchecked` or `no_module`. - #[cfg(not(feature = "no_module"))] + /// Zero under `no_module`. #[inline(always)] #[must_use] pub const fn max_modules(&self) -> usize { - self.limits.max_modules + #[cfg(not(feature = "no_module"))] + return self.limits.max_modules; + #[cfg(feature = "no_module")] + return 0; } /// Set the depth limits for expressions (0 for unlimited). /// @@ -157,8 +161,6 @@ impl Engine { self } /// The depth limit for expressions (0 for unlimited). - /// - /// Not available under `unchecked`. #[inline] #[must_use] pub const fn max_expr_depth(&self) -> usize { @@ -170,16 +172,18 @@ impl Engine { } /// The depth limit for expressions in functions (0 for unlimited). /// - /// Not available under `unchecked` or `no_function`. - #[cfg(not(feature = "no_function"))] + /// Zero under `no_function`. #[inline] #[must_use] pub const fn max_function_expr_depth(&self) -> usize { - if let Some(n) = self.limits.max_function_expr_depth { + #[cfg(not(feature = "no_function"))] + return if let Some(n) = self.limits.max_function_expr_depth { n.get() } else { 0 - } + }; + #[cfg(feature = "no_function")] + return 0; } /// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited). /// @@ -190,8 +194,6 @@ impl Engine { self } /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). - /// - /// Not available under `unchecked`. #[inline] #[must_use] pub const fn max_string_size(&self) -> usize { @@ -212,16 +214,18 @@ impl Engine { } /// The maximum length of [arrays][crate::Array] (0 for unlimited). /// - /// Not available under `unchecked` or `no_index`. - #[cfg(not(feature = "no_index"))] + /// Zero under `no_index`. #[inline] #[must_use] pub const fn max_array_size(&self) -> usize { - if let Some(n) = self.limits.max_array_size { + #[cfg(not(feature = "no_index"))] + return if let Some(n) = self.limits.max_array_size { n.get() } else { 0 - } + }; + #[cfg(feature = "no_index")] + return 0; } /// Set the maximum size of [object maps][crate::Map] (0 for unlimited). /// @@ -234,15 +238,17 @@ impl Engine { } /// The maximum size of [object maps][crate::Map] (0 for unlimited). /// - /// Not available under `unchecked` or `no_object`. - #[cfg(not(feature = "no_object"))] + /// Zero under `no_object`. #[inline] #[must_use] pub const fn max_map_size(&self) -> usize { - if let Some(n) = self.limits.max_map_size { + #[cfg(not(feature = "no_object"))] + return if let Some(n) = self.limits.max_map_size { n.get() } else { 0 - } + }; + #[cfg(feature = "no_object")] + return 0; } } diff --git a/src/api/mod.rs b/src/api/mod.rs index 6ddf090b..d98b0dd6 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -236,3 +236,55 @@ impl Engine { self } } + +#[cfg(feature = "unchecked")] +impl Engine { + /// The maximum levels of function calls allowed for a script. + #[inline(always)] + #[must_use] + pub const fn max_call_levels(&self) -> usize { + usize::MAX + } + /// The maximum number of operations allowed for a script to run (0 for unlimited). + #[inline] + #[must_use] + pub const fn max_operations(&self) -> u64 { + 0 + } + /// The maximum number of imported [modules][crate::Module] allowed for a script. + #[inline(always)] + #[must_use] + pub const fn max_modules(&self) -> usize { + usize::MAX + } + /// The depth limit for expressions (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_expr_depth(&self) -> usize { + 0 + } + /// The depth limit for expressions in functions (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_function_expr_depth(&self) -> usize { + 0 + } + /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_string_size(&self) -> usize { + 0 + } + /// The maximum length of [arrays][crate::Array] (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_array_size(&self) -> usize { + 0 + } + /// The maximum size of [object maps][crate::Map] (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_map_size(&self) -> usize { + 0 + } +} diff --git a/src/api/register.rs b/src/api/register.rs index 9095a31f..ce5c0c4a 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -226,12 +226,12 @@ impl Engine { #[inline(always)] pub fn register_type_with_name_raw( &mut self, - fully_qualified_type_path: impl Into, + type_path: impl Into, name: impl Into, ) -> &mut Self { // Add the pretty-print type name into the map self.global_namespace_mut() - .set_custom_type_raw(fully_qualified_type_path, name); + .set_custom_type_raw(type_path, name); self } /// Register a type iterator for an iterable type with the [`Engine`]. diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 3d271af2..ef7edf84 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -36,6 +36,7 @@ pub struct GlobalRuntimeState<'a> { /// Number of operations performed. pub num_operations: u64, /// Number of modules loaded. + #[cfg(not(feature = "no_module"))] pub num_modules_loaded: usize, /// Level of the current scope. /// diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 018a6bf4..d62a39bb 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -738,7 +738,7 @@ impl Engine { #[cfg(feature = "no_object")] _ => { - err.take_position(); + let _ = err.take_position(); err.to_string().into() } #[cfg(not(feature = "no_object"))] @@ -924,7 +924,6 @@ impl Engine { let (expr, export) = &**x; // Guard against too many modules - #[cfg(not(feature = "unchecked"))] if global.num_modules_loaded >= self.max_modules() { return Err(ERR::ErrorTooManyModules(*_pos).into()); } diff --git a/src/func/script.rs b/src/func/script.rs index 8d8139d6..6d016cc5 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -67,7 +67,6 @@ impl Engine { self.inc_operations(&mut global.num_operations, pos)?; // Check for stack overflow - #[cfg(not(feature = "unchecked"))] if level > self.max_call_levels() { return Err(ERR::ErrorStackOverflow(pos).into()); } diff --git a/src/module/mod.rs b/src/module/mod.rs index 0479ee70..9e5afca5 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -494,12 +494,12 @@ impl Module { #[inline(always)] pub fn set_custom_type_raw( &mut self, - type_name: impl Into, + type_path: impl Into, name: impl Into, ) -> &mut Self { self.custom_types .get_or_insert_with(CustomTypesCollection::new) - .add(type_name, name); + .add(type_path, name); self } /// Get the display name of a registered custom type. diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 4b6639c2..4ddd68b6 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -79,7 +79,6 @@ pub mod blob_functions { let _ctx = ctx; // Check if blob will be over max size limit - #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { return Err( crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), @@ -364,7 +363,6 @@ pub mod blob_functions { let _ctx = ctx; // Check if blob will be over max size limit - #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { return Err( crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 93e8edf4..cfaf3e83 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -1216,7 +1216,6 @@ mod string_functions { let _ctx = ctx; // Check if string will be over max size limit - #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() { return Err(crate::ERR::ErrorDataTooLarge( "Length of string".to_string(), @@ -1234,7 +1233,6 @@ mod string_functions { p.push(character); } - #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() { return Err(crate::ERR::ErrorDataTooLarge( @@ -1278,7 +1276,6 @@ mod string_functions { let _ctx = ctx; // Check if string will be over max size limit - #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() { return Err(crate::ERR::ErrorDataTooLarge( "Length of string".to_string(), @@ -1303,7 +1300,6 @@ mod string_functions { } } - #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() { return Err(crate::ERR::ErrorDataTooLarge( diff --git a/src/parser.rs b/src/parser.rs index 0ec27614..77037327 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -906,7 +906,6 @@ impl Engine { loop { const MISSING_RBRACKET: &str = "to end this array literal"; - #[cfg(not(feature = "unchecked"))] if self.max_array_size() > 0 && array.len() >= self.max_array_size() { return Err(PERR::LiteralTooLarge( "Size of array literal".to_string(), @@ -1043,7 +1042,6 @@ impl Engine { } }; - #[cfg(not(feature = "unchecked"))] if self.max_map_size() > 0 && map.len() >= self.max_map_size() { return Err(PERR::LiteralTooLarge( "Number of properties in object map literal".to_string(), From 80f95b6f2a853cc5e8b97a093f78b71c0d2abb16 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Oct 2022 16:27:04 +0800 Subject: [PATCH 023/116] Fix no_module. --- src/eval/global_state.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index ef7edf84..12e2ce5f 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -86,6 +86,7 @@ impl GlobalRuntimeState<'_> { modules: crate::StaticVec::new_const(), source: Identifier::new_const(), num_operations: 0, + #[cfg(not(feature = "no_module"))] num_modules_loaded: 0, scope_level: 0, always_search_scope: false, @@ -361,14 +362,14 @@ impl fmt::Debug for GlobalRuntimeState<'_> { f.field("imports", &self.keys.iter().zip(self.modules.iter())); f.field("source", &self.source) - .field("num_operations", &self.num_operations) - .field("num_modules_loaded", &self.num_modules_loaded); + .field("num_operations", &self.num_operations); #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] f.field("fn_hash_indexing", &self.fn_hash_indexing); #[cfg(not(feature = "no_module"))] - f.field("embedded_module_resolver", &self.embedded_module_resolver); + f.field("num_modules_loaded", &self.num_modules_loaded) + .field("embedded_module_resolver", &self.embedded_module_resolver); #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] From 42c0eeed5760cca5c8404895c3617c1a9af7f29d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Oct 2022 15:07:54 +0800 Subject: [PATCH 024/116] Remove unnecessary unchecked gates. --- src/api/limits.rs | 21 +++++++++- src/api/mod.rs | 75 +--------------------------------- src/engine.rs | 80 ++++++++++++++++++++++++++++++++++++- src/eval/chaining.rs | 13 ++---- src/eval/data_check.rs | 33 +++++---------- src/eval/expr.rs | 56 ++++++++++++++------------ src/eval/stmt.rs | 26 ++++-------- src/func/builtin.rs | 4 +- src/func/call.rs | 19 +++------ src/func/script.rs | 3 +- src/packages/arithmetic.rs | 54 ++++++++----------------- src/packages/array_basic.rs | 5 +-- 12 files changed, 178 insertions(+), 211 deletions(-) diff --git a/src/api/limits.rs b/src/api/limits.rs index 70de6592..8f6f2be9 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -1,12 +1,31 @@ //! Settings for [`Engine`]'s limitations. #![cfg(not(feature = "unchecked"))] -use super::default_limits; use crate::Engine; use std::num::{NonZeroU64, NonZeroUsize}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +pub mod default_limits { + #[cfg(debug_assertions)] + #[cfg(not(feature = "no_function"))] + pub const MAX_CALL_STACK_DEPTH: usize = 8; + #[cfg(debug_assertions)] + pub const MAX_EXPR_DEPTH: usize = 32; + #[cfg(not(feature = "no_function"))] + #[cfg(debug_assertions)] + pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; + + #[cfg(not(debug_assertions))] + #[cfg(not(feature = "no_function"))] + pub const MAX_CALL_STACK_DEPTH: usize = 64; + #[cfg(not(debug_assertions))] + pub const MAX_EXPR_DEPTH: usize = 64; + #[cfg(not(feature = "no_function"))] + #[cfg(not(debug_assertions))] + pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; +} + /// A type containing all the limits imposed by the [`Engine`]. /// /// Not available under `unchecked`. diff --git a/src/api/mod.rs b/src/api/mod.rs index d98b0dd6..c296c81e 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -43,28 +43,7 @@ use std::prelude::v1::*; pub mod default_limits { #[cfg(not(feature = "unchecked"))] - #[cfg(debug_assertions)] - #[cfg(not(feature = "no_function"))] - pub const MAX_CALL_STACK_DEPTH: usize = 8; - #[cfg(not(feature = "unchecked"))] - #[cfg(debug_assertions)] - pub const MAX_EXPR_DEPTH: usize = 32; - #[cfg(not(feature = "unchecked"))] - #[cfg(not(feature = "no_function"))] - #[cfg(debug_assertions)] - pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; - - #[cfg(not(feature = "unchecked"))] - #[cfg(not(debug_assertions))] - #[cfg(not(feature = "no_function"))] - pub const MAX_CALL_STACK_DEPTH: usize = 64; - #[cfg(not(feature = "unchecked"))] - #[cfg(not(debug_assertions))] - pub const MAX_EXPR_DEPTH: usize = 64; - #[cfg(not(feature = "unchecked"))] - #[cfg(not(feature = "no_function"))] - #[cfg(not(debug_assertions))] - pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; + pub use super::limits::default_limits::*; pub const MAX_DYNAMIC_PARAMETERS: usize = 16; } @@ -236,55 +215,3 @@ impl Engine { self } } - -#[cfg(feature = "unchecked")] -impl Engine { - /// The maximum levels of function calls allowed for a script. - #[inline(always)] - #[must_use] - pub const fn max_call_levels(&self) -> usize { - usize::MAX - } - /// The maximum number of operations allowed for a script to run (0 for unlimited). - #[inline] - #[must_use] - pub const fn max_operations(&self) -> u64 { - 0 - } - /// The maximum number of imported [modules][crate::Module] allowed for a script. - #[inline(always)] - #[must_use] - pub const fn max_modules(&self) -> usize { - usize::MAX - } - /// The depth limit for expressions (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_expr_depth(&self) -> usize { - 0 - } - /// The depth limit for expressions in functions (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_function_expr_depth(&self) -> usize { - 0 - } - /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_string_size(&self) -> usize { - 0 - } - /// The maximum length of [arrays][crate::Array] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_array_size(&self) -> usize { - 0 - } - /// The maximum size of [object maps][crate::Map] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_map_size(&self) -> usize { - 0 - } -} diff --git a/src/engine.rs b/src/engine.rs index cb4cb094..68ec87cc 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -347,9 +347,9 @@ impl Engine { } /// Check a result to ensure that it is valid. + #[cfg(not(feature = "unchecked"))] #[inline] pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult { - #[cfg(not(feature = "unchecked"))] if let Ok(ref r) = result { self.check_data_size(r, _pos)?; } @@ -357,3 +357,81 @@ impl Engine { result } } + +#[cfg(feature = "unchecked")] +impl Engine { + /// The maximum levels of function calls allowed for a script. + #[inline(always)] + #[must_use] + pub const fn max_call_levels(&self) -> usize { + usize::MAX + } + /// The maximum number of operations allowed for a script to run (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_operations(&self) -> u64 { + 0 + } + /// The maximum number of imported [modules][crate::Module] allowed for a script. + #[inline(always)] + #[must_use] + pub const fn max_modules(&self) -> usize { + usize::MAX + } + /// The depth limit for expressions (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_expr_depth(&self) -> usize { + 0 + } + /// The depth limit for expressions in functions (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_function_expr_depth(&self) -> usize { + 0 + } + /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_string_size(&self) -> usize { + 0 + } + /// The maximum length of [arrays][crate::Array] (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_array_size(&self) -> usize { + 0 + } + /// The maximum size of [object maps][crate::Map] (0 for unlimited). + #[inline(always)] + #[must_use] + pub const fn max_map_size(&self) -> usize { + 0 + } + + /// Check if the number of operations stay within limit. + #[inline(always)] + pub(crate) const fn track_operation( + &self, + _: &crate::GlobalRuntimeState, + _: Position, + ) -> crate::RhaiResultOf<()> { + Ok(()) + } + + /// Check whether the size of a [`Dynamic`] is within limits. + #[inline(always)] + pub(crate) const fn check_data_size( + &self, + _: &Dynamic, + _: Position, + ) -> crate::RhaiResultOf<()> { + Ok(()) + } + + /// Check a result to ensure that it is valid. + #[inline(always)] + pub(crate) const fn check_return_value(&self, result: RhaiResult, _: Position) -> RhaiResult { + result + } +} diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 2b9d6425..78acb0ff 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -131,7 +131,6 @@ impl Engine { self.eval_op_assignment( global, caches, lib, op_info, obj_ptr, root, new_val, level, )?; - #[cfg(not(feature = "unchecked"))] self.check_data_size(obj_ptr, op_info.pos)?; None } @@ -159,7 +158,6 @@ impl Engine { )?; // Replace new value new_val = val.take_or_clone(); - #[cfg(not(feature = "unchecked"))] self.check_data_size(&new_val, op_info.pos)?; } } @@ -246,7 +244,6 @@ impl Engine { global, caches, lib, op_info, val_target, root, new_val, level, )?; } - #[cfg(not(feature = "unchecked"))] self.check_data_size(target.source(), op_info.pos)?; Ok((Dynamic::UNIT, true)) } @@ -606,9 +603,7 @@ impl Engine { Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, lhs, level)?; - - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, *var_pos)?; + self.track_operation(global, *var_pos)?; let (mut target, ..) = self.search_namespace(scope, global, lib, this_ptr, lhs, level)?; @@ -655,8 +650,7 @@ impl Engine { idx_values: &mut FnArgsVec, level: usize, ) -> RhaiResultOf<()> { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; + self.track_operation(global, expr.position())?; match expr { #[cfg(not(feature = "no_object"))] @@ -814,8 +808,7 @@ impl Engine { use_indexers: bool, level: usize, ) -> RhaiResultOf> { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, Position::NONE)?; + self.track_operation(global, Position::NONE)?; match target { #[cfg(not(feature = "no_index"))] diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 293b1d99..377a7757 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -1,6 +1,7 @@ //! Data size checks during evaluation. #![cfg(not(feature = "unchecked"))] +use super::GlobalRuntimeState; use crate::types::dynamic::Union; use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR}; use std::num::NonZeroUsize; @@ -15,7 +16,6 @@ impl Engine { /// # Panics /// /// Panics if any interior data is shared (should never happen). - #[cfg(not(feature = "unchecked"))] pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) { match value.0 { #[cfg(not(feature = "no_index"))] @@ -71,24 +71,11 @@ impl Engine { } /// Is there a data size limit set? - #[cfg(not(feature = "unchecked"))] pub(crate) const fn has_data_size_limit(&self) -> bool { - let mut _limited = self.limits.max_string_size.is_some(); - - #[cfg(not(feature = "no_index"))] - { - _limited = _limited || self.limits.max_array_size.is_some(); - } - #[cfg(not(feature = "no_object"))] - { - _limited = _limited || self.limits.max_map_size.is_some(); - } - - _limited + self.max_string_size() > 0 || self.max_array_size() > 0 || self.max_map_size() > 0 } /// Raise an error if any data size exceeds limit. - #[cfg(not(feature = "unchecked"))] pub(crate) fn raise_err_if_over_data_size_limit( &self, sizes: (usize, usize, usize), @@ -128,7 +115,6 @@ impl Engine { } /// Check whether the size of a [`Dynamic`] is within limits. - #[cfg(not(feature = "unchecked"))] pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { // If no data size limits, just return if !self.has_data_size_limit() { @@ -143,29 +129,30 @@ impl Engine { /// Raise an error if the size of a [`Dynamic`] is out of limits (if any). /// /// Not available under `unchecked`. - #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> { self.check_data_size(value, Position::NONE) } /// Check if the number of operations stay within limit. - #[cfg(not(feature = "unchecked"))] - pub(crate) fn inc_operations( + pub(crate) fn track_operation( &self, - num_operations: &mut u64, + global: &mut GlobalRuntimeState, pos: Position, ) -> RhaiResultOf<()> { - *num_operations += 1; + global.num_operations += 1; // Guard against too many operations - if self.max_operations() > 0 && *num_operations > self.max_operations() { + let max = self.max_operations(); + let num_operations = global.num_operations; + + if max > 0 && num_operations > max { return Err(ERR::ErrorTooManyOperations(pos).into()); } // Report progress - only in steps if let Some(ref progress) = self.progress { - if let Some(token) = progress(*num_operations) { + if let Some(token) = progress(num_operations) { // Terminate script if progress returns a termination token return Err(ERR::ErrorTerminated(token, pos).into()); } diff --git a/src/eval/expr.rs b/src/eval/expr.rs index c965ddda..ce34a8ad 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -318,8 +318,7 @@ impl Engine { let reset_debugger = self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?; - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; + self.track_operation(global, expr.position())?; let result = self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); @@ -337,8 +336,7 @@ impl Engine { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, expr, level)?; - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; + self.track_operation(global, expr.position())?; return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS { this_ptr @@ -355,8 +353,7 @@ impl Engine { let reset_debugger = self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?; - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; + self.track_operation(global, expr.position())?; let result = match expr { // Constants @@ -410,7 +407,7 @@ impl Engine { let mut result = Ok(Dynamic::UNIT); #[cfg(not(feature = "unchecked"))] - let mut sizes = (0, 0, 0); + let mut total_data_sizes = (0, 0, 0); for item_expr in &**x { let value = match self @@ -424,19 +421,21 @@ impl Engine { }; #[cfg(not(feature = "unchecked"))] - let val_sizes = Self::calc_data_sizes(&value, true); + if self.has_data_size_limit() { + let val_sizes = Self::calc_data_sizes(&value, true); + + total_data_sizes = ( + total_data_sizes.0 + val_sizes.0, + total_data_sizes.1 + val_sizes.1, + total_data_sizes.2 + val_sizes.2, + ); + self.raise_err_if_over_data_size_limit( + total_data_sizes, + item_expr.position(), + )?; + } array.push(value); - - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - sizes = ( - sizes.0 + val_sizes.0, - sizes.1 + val_sizes.1, - sizes.2 + val_sizes.2, - ); - self.raise_err_if_over_data_size_limit(sizes, item_expr.position())?; - } } result.map(|_| array.into()) @@ -448,7 +447,7 @@ impl Engine { let mut result = Ok(Dynamic::UNIT); #[cfg(not(feature = "unchecked"))] - let mut sizes = (0, 0, 0); + let mut total_data_sizes = (0, 0, 0); for (key, value_expr) in &x.0 { let value = match self @@ -462,15 +461,20 @@ impl Engine { }; #[cfg(not(feature = "unchecked"))] - let delta = Self::calc_data_sizes(&value, true); + if self.has_data_size_limit() { + let delta = Self::calc_data_sizes(&value, true); + total_data_sizes = ( + total_data_sizes.0 + delta.0, + total_data_sizes.1 + delta.1, + total_data_sizes.2 + delta.2, + ); + self.raise_err_if_over_data_size_limit( + total_data_sizes, + value_expr.position(), + )?; + } *map.get_mut(key.as_str()).unwrap() = value; - - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - sizes = (sizes.0 + delta.0, sizes.1 + delta.1, sizes.2 + delta.2); - self.raise_err_if_over_data_size_limit(sizes, value_expr.position())?; - } } result.map(|_| map.into()) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index d62a39bb..726b661f 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -152,7 +152,6 @@ impl Engine { let context = (self, op, None, &*global, lib, *op_pos, level).into(); let result = func(context, args).map(|_| ()); - #[cfg(not(feature = "unchecked"))] self.check_data_size(args[0], root.1)?; return result; @@ -165,10 +164,7 @@ impl Engine { match self.call_native_fn( global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { - Ok(_) => { - #[cfg(not(feature = "unchecked"))] - self.check_data_size(args[0], root.1)?; - } + Ok(_) => self.check_data_size(args[0], root.1)?, Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` @@ -218,8 +214,7 @@ impl Engine { // Function calls should account for a relatively larger portion of statements. if let Stmt::FnCall(x, ..) = stmt { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, stmt.position())?; + self.track_operation(global, stmt.position())?; let result = self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); @@ -236,8 +231,7 @@ impl Engine { if let Stmt::Assignment(x, ..) = stmt { let (op_info, BinaryExpr { lhs, rhs }) = &**x; - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, stmt.position())?; + self.track_operation(global, stmt.position())?; let result = if let Expr::Variable(x, ..) = lhs { let rhs_result = self @@ -267,8 +261,7 @@ impl Engine { ); } - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, pos)?; + self.track_operation(global, pos)?; let root = (var_name, pos); let lhs_ptr = &mut lhs_ptr; @@ -339,8 +332,7 @@ impl Engine { return result; } - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, stmt.position())?; + self.track_operation(global, stmt.position())?; let result = match stmt { // No-op @@ -512,8 +504,7 @@ impl Engine { let (.., body) = &**x; if body.is_empty() { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, body.position())?; + self.track_operation(global, body.position())?; } else { match self .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) @@ -668,10 +659,7 @@ impl Engine { *scope.get_mut_by_index(index).write_lock().unwrap() = value; - #[cfg(not(feature = "unchecked"))] - if let Err(err) = self - .inc_operations(&mut global.num_operations, statements.position()) - { + if let Err(err) = self.track_operation(global, statements.position()) { loop_result = Err(err); break; } diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 075706fe..6b16366f 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -298,7 +298,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< ($x:ty, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { #[cfg(not(feature = "unchecked"))] - use crate::packages::arithmetic::decimal_functions::*; + use crate::packages::arithmetic::decimal_functions::builtin::*; #[cfg(not(feature = "unchecked"))] match op { @@ -697,7 +697,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt ($x:ident, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { #[cfg(not(feature = "unchecked"))] - use crate::packages::arithmetic::decimal_functions::*; + use crate::packages::arithmetic::decimal_functions::builtin::*; #[cfg(not(feature = "unchecked"))] return match op { diff --git a/src/func/call.rs b/src/func/call.rs index ea6349fe..c093c237 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -358,8 +358,7 @@ impl Engine { pos: Position, level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, pos)?; + self.track_operation(global, pos)?; let parent_source = global.source.clone(); let op_assign = if is_op_assign { @@ -1232,8 +1231,7 @@ impl Engine { target = target.into_owned(); } - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, _pos)?; + self.track_operation(global, _pos)?; #[cfg(not(feature = "no_closure"))] let target_is_shared = target.is_shared(); @@ -1312,8 +1310,7 @@ impl Engine { let (target, _pos) = self.search_scope_only(scope, global, lib, this_ptr, first_arg, level)?; - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, _pos)?; + self.track_operation(global, _pos)?; #[cfg(not(feature = "no_closure"))] let target_is_shared = target.is_shared(); @@ -1350,9 +1347,7 @@ impl Engine { let mut func = match module.get_qualified_fn(hash) { // Then search native Rust functions None => { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, pos)?; - + self.track_operation(global, pos)?; let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id())); let hash_qualified_fn = combine_hashes(hash, hash_params); @@ -1384,8 +1379,7 @@ impl Engine { })); let hash_qualified_fn = combine_hashes(hash, hash_params); - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, pos)?; + self.track_operation(global, pos)?; if let Some(f) = module.get_qualified_fn(hash_qualified_fn) { func = Some(f); @@ -1461,8 +1455,7 @@ impl Engine { _pos: Position, level: usize, ) -> RhaiResult { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, _pos)?; + self.track_operation(global, _pos)?; let script = script.trim(); diff --git a/src/func/script.rs b/src/func/script.rs index 6d016cc5..c387b3dc 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -63,8 +63,7 @@ impl Engine { assert!(fn_def.params.len() == args.len()); - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, pos)?; + self.track_operation(global, pos)?; // Check for stack overflow if level > self.max_call_levels() { diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 2cba468e..3dafc1ae 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -488,39 +488,29 @@ mod f64_functions { #[cfg(feature = "decimal")] #[export_module] pub mod decimal_functions { - use num_traits::Pow; - use rust_decimal::{prelude::Zero, Decimal, MathematicalOps}; + use rust_decimal::{prelude::Zero, Decimal}; - #[rhai_fn(skip, return_raw)] - pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) { + #[cfg(not(feature = "unchecked"))] + pub mod builtin { + use rust_decimal::MathematicalOps; + + #[rhai_fn(return_raw)] + pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf { x.checked_add(y) .ok_or_else(|| make_err(format!("Addition overflow: {x} + {y}"))) - } else { - Ok(x + y) } - } - #[rhai_fn(skip, return_raw)] - pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) { + #[rhai_fn(return_raw)] + pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf { x.checked_sub(y) .ok_or_else(|| make_err(format!("Subtraction overflow: {x} - {y}"))) - } else { - Ok(x - y) } - } - #[rhai_fn(skip, return_raw)] - pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) { + #[rhai_fn(return_raw)] + pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf { x.checked_mul(y) .ok_or_else(|| make_err(format!("Multiplication overflow: {x} * {y}"))) - } else { - Ok(x * y) } - } - #[rhai_fn(skip, return_raw)] - pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) { + #[rhai_fn(return_raw)] + pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf { // Detect division by zero if y == Decimal::zero() { Err(make_err(format!("Division by zero: {x} / {y}"))) @@ -528,26 +518,16 @@ pub mod decimal_functions { x.checked_div(y) .ok_or_else(|| make_err(format!("Division overflow: {x} / {y}"))) } - } else { - Ok(x / y) } - } - #[rhai_fn(skip, return_raw)] - pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) { + #[rhai_fn(return_raw)] + pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf { x.checked_rem(y) .ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {x} % {y}"))) - } else { - Ok(x % y) } - } - #[rhai_fn(skip, return_raw)] - pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) { + #[rhai_fn(return_raw)] + pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf { x.checked_powd(y) .ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}"))) - } else { - Ok(x.pow(y)) } } #[rhai_fn(name = "-")] diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 72e2347e..7e639275 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -236,9 +236,8 @@ pub mod array_functions { } let check_sizes = match item.0 { - crate::types::dynamic::Union::Array(..) | crate::types::dynamic::Union::Str(..) => { - true - } + crate::types::dynamic::Union::Str(..) => true, + crate::types::dynamic::Union::Array(..) => true, #[cfg(not(feature = "no_object"))] crate::types::dynamic::Union::Map(..) => true, _ => false, From 796206f293d97e2f1bed355bcfac79f4c371e210 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Oct 2022 15:15:42 +0800 Subject: [PATCH 025/116] Fix build. --- src/engine.rs | 2 +- src/parser.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 68ec87cc..323f05b5 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -413,7 +413,7 @@ impl Engine { #[inline(always)] pub(crate) const fn track_operation( &self, - _: &crate::GlobalRuntimeState, + _: &crate::eval::GlobalRuntimeState, _: Position, ) -> crate::RhaiResultOf<()> { Ok(()) diff --git a/src/parser.rs b/src/parser.rs index 77037327..798067c9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -34,10 +34,6 @@ pub type ParseResult = Result; type FnLib = StraightHashMap>; -const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax(); - -const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax(); - /// Invalid variable name that acts as a search barrier in a [`Scope`]. const SCOPE_SEARCH_BARRIER_MARKER: &str = "$ BARRIER $"; @@ -2468,6 +2464,8 @@ impl Engine { pos: Position, ) -> ParseResult { use crate::api::custom_syntax::markers::*; + const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax(); + const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax(); let mut settings = settings; let mut inputs = StaticVec::new_const(); From d6b0f9978196f7e8e0f73082deff8be02c2d2528 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 10 Oct 2022 16:46:35 +0800 Subject: [PATCH 026/116] Refactor. --- CHANGELOG.md | 2 +- src/api/limits.rs | 63 ++++++++++++++++++++++-------------- src/bin/rhai-dbg.rs | 30 +++++++---------- src/bin/rhai-repl.rs | 27 +++++++++------- src/eval/data_check.rs | 58 ++++++++++++++------------------- src/eval/debugger.rs | 11 +++---- src/eval/global_state.rs | 7 ++-- src/func/hashing.rs | 9 ++---- src/module/mod.rs | 14 ++++---- src/module/resolvers/file.rs | 7 ++-- src/optimizer.rs | 43 ++++++++++++------------ src/packages/map_basic.rs | 9 +++--- src/packages/string_more.rs | 26 ++++++++------- src/serde/deserialize.rs | 7 ++-- src/serde/serialize.rs | 14 ++++---- 15 files changed, 161 insertions(+), 166 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1fe04c4..9c2848dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ Enhancements * `Scope` is now serializable and deserializable via `serde`. * `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline. * `parse_json` function is added to parse a JSON string into an object map. -* Methods return maximum limits (e.g. `max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code. +* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code. Version 1.10.1 diff --git a/src/api/limits.rs b/src/api/limits.rs index 8f6f2be9..32daf13e 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -101,6 +101,27 @@ impl Default for Limits { } impl Engine { + /// Is there a data size limit set? + #[inline] + pub(crate) const fn has_data_size_limit(&self) -> bool { + self.limits.max_string_size.is_some() + || { + #[cfg(not(feature = "no_index"))] + { + self.limits.max_array_size.is_some() + } + #[cfg(feature = "no_index")] + false + } + || { + #[cfg(not(feature = "no_object"))] + { + self.limits.max_map_size.is_some() + } + #[cfg(feature = "no_object")] + false + } + } /// Set the maximum levels of function calls allowed for a script in order to avoid /// infinite recursion and stack overflows. /// @@ -137,10 +158,9 @@ impl Engine { #[inline] #[must_use] pub const fn max_operations(&self) -> u64 { - if let Some(n) = self.limits.max_operations { - n.get() - } else { - 0 + match self.limits.max_operations { + Some(n) => n.get(), + None => 0, } } /// Set the maximum number of imported [modules][crate::Module] allowed for a script. @@ -183,10 +203,9 @@ impl Engine { #[inline] #[must_use] pub const fn max_expr_depth(&self) -> usize { - if let Some(n) = self.limits.max_expr_depth { - n.get() - } else { - 0 + match self.limits.max_expr_depth { + Some(n) => n.get(), + None => 0, } } /// The depth limit for expressions in functions (0 for unlimited). @@ -196,10 +215,9 @@ impl Engine { #[must_use] pub const fn max_function_expr_depth(&self) -> usize { #[cfg(not(feature = "no_function"))] - return if let Some(n) = self.limits.max_function_expr_depth { - n.get() - } else { - 0 + return match self.limits.max_function_expr_depth { + Some(n) => n.get(), + None => 0, }; #[cfg(feature = "no_function")] return 0; @@ -216,10 +234,9 @@ impl Engine { #[inline] #[must_use] pub const fn max_string_size(&self) -> usize { - if let Some(n) = self.limits.max_string_size { - n.get() - } else { - 0 + match self.limits.max_string_size { + Some(n) => n.get(), + None => 0, } } /// Set the maximum length of [arrays][crate::Array] (0 for unlimited). @@ -238,10 +255,9 @@ impl Engine { #[must_use] pub const fn max_array_size(&self) -> usize { #[cfg(not(feature = "no_index"))] - return if let Some(n) = self.limits.max_array_size { - n.get() - } else { - 0 + return match self.limits.max_array_size { + Some(n) => n.get(), + None => 0, }; #[cfg(feature = "no_index")] return 0; @@ -262,10 +278,9 @@ impl Engine { #[must_use] pub const fn max_map_size(&self) -> usize { #[cfg(not(feature = "no_object"))] - return if let Some(n) = self.limits.max_map_size { - n.get() - } else { - 0 + return match self.limits.max_map_size { + Some(n) => n.get(), + None => 0, }; #[cfg(feature = "no_object")] return 0; diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index a83b680c..71d5152a 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -45,7 +45,6 @@ fn print_source(lines: &[String], pos: Position, offset: usize, window: (usize, if n == line { if let Some(pos) = pos.position() { let shift = offset + line_no_len + marker.len() + 2; - println!("{0:>1$}{2:>3$}", "│ ", shift, "\x1b[36m^\x1b[39m", pos + 10); } } @@ -310,10 +309,11 @@ fn debug_callback( ["node"] => { if pos.is_none() { println!("{:?}", node); - } else if let Some(source) = source { - println!("{:?} {} @ {:?}", node, source, pos); } else { - println!("{:?} @ {:?}", node, pos); + match source { + Some(source) => println!("{:?} {} @ {:?}", node, source, pos), + None => println!("{:?} @ {:?}", node, pos), + } } println!(); } @@ -339,20 +339,14 @@ fn debug_callback( ["over" | "o"] => break Ok(DebuggerCommand::StepOver), ["next" | "n"] => break Ok(DebuggerCommand::Next), ["scope"] => println!("{}", context.scope()), - ["print" | "p", "this"] => { - if let Some(value) = context.this_ptr() { - println!("=> {:?}", value); - } else { - println!("`this` pointer is unbound."); - } - } - ["print" | "p", var_name] => { - if let Some(value) = context.scope().get_value::(var_name) { - println!("=> {:?}", value); - } else { - eprintln!("Variable not found: {}", var_name); - } - } + ["print" | "p", "this"] => match context.this_ptr() { + Some(value) => println!("=> {:?}", value), + None => println!("`this` pointer is unbound."), + }, + ["print" | "p", var_name] => match context.scope().get_value::(var_name) { + Some(value) => println!("=> {:?}", value), + None => eprintln!("Variable not found: {}", var_name), + }, ["print" | "p"] => { println!("{}", context.scope().clone_visible()); if let Some(value) = context.this_ptr() { diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 00e01e8b..dcab9974 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -482,27 +482,30 @@ fn main() { continue; } "!!" => { - if let Some(line) = rl.history().last() { - replacement = Some(line.clone()); - replacement_index = history_offset + rl.history().len() - 1; - } else { - eprintln!("No lines history!"); + match rl.history().last() { + Some(line) => { + replacement = Some(line.clone()); + replacement_index = history_offset + rl.history().len() - 1; + } + None => eprintln!("No lines history!"), } continue; } _ if cmd.starts_with("!?") => { let text = cmd[2..].trim(); - if let Some((n, line)) = rl + let history = rl .history() .iter() .rev() .enumerate() - .find(|&(.., h)| h.contains(text)) - { - replacement = Some(line.clone()); - replacement_index = history_offset + (rl.history().len() - 1 - n); - } else { - eprintln!("History line not found: {}", text); + .find(|&(.., h)| h.contains(text)); + + match history { + Some((n, line)) => { + replacement = Some(line.clone()); + replacement_index = history_offset + (rl.history().len() - 1 - n); + } + None => eprintln!("History line not found: {}", text), } continue; } diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 377a7757..76cf7a5c 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -4,7 +4,6 @@ use super::GlobalRuntimeState; use crate::types::dynamic::Union; use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR}; -use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -21,40 +20,40 @@ impl Engine { #[cfg(not(feature = "no_index"))] Union::Array(ref arr, ..) => { arr.iter() - .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { + .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { Union::Array(..) => { let (a, m, s) = Self::calc_data_sizes(value, false); - (arrays + a + 1, maps + m, strings + s) + (ax + a + 1, mx + m, sx + s) } - Union::Blob(ref a, ..) => (arrays + 1 + a.len(), maps, strings), + Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), #[cfg(not(feature = "no_object"))] Union::Map(..) => { let (a, m, s) = Self::calc_data_sizes(value, false); - (arrays + a + 1, maps + m, strings + s) + (ax + a + 1, mx + m, sx + s) } - Union::Str(ref s, ..) => (arrays + 1, maps, strings + s.len()), - _ => (arrays + 1, maps, strings), + Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), + _ => (ax + 1, mx, sx), }) } #[cfg(not(feature = "no_index"))] - Union::Blob(ref arr, ..) => (arr.len(), 0, 0), + Union::Blob(ref blob, ..) => (blob.len(), 0, 0), #[cfg(not(feature = "no_object"))] Union::Map(ref map, ..) => { map.values() - .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { + .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { #[cfg(not(feature = "no_index"))] Union::Array(..) => { let (a, m, s) = Self::calc_data_sizes(value, false); - (arrays + a, maps + m + 1, strings + s) + (ax + a, mx + m + 1, sx + s) } #[cfg(not(feature = "no_index"))] - Union::Blob(ref a, ..) => (arrays + a.len(), maps, strings), + Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), Union::Map(..) => { let (a, m, s) = Self::calc_data_sizes(value, false); - (arrays + a, maps + m + 1, strings + s) + (ax + a, mx + m + 1, sx + s) } - Union::Str(ref s, ..) => (arrays, maps + 1, strings + s.len()), - _ => (arrays, maps + 1, strings), + Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), + _ => (ax, mx + 1, sx), }) } Union::Str(ref s, ..) => (0, 0, s.len()), @@ -70,43 +69,34 @@ impl Engine { } } - /// Is there a data size limit set? - pub(crate) const fn has_data_size_limit(&self) -> bool { - self.max_string_size() > 0 || self.max_array_size() > 0 || self.max_map_size() > 0 - } - /// Raise an error if any data size exceeds limit. pub(crate) fn raise_err_if_over_data_size_limit( &self, - sizes: (usize, usize, usize), + (_arr, _map, s): (usize, usize, usize), pos: Position, ) -> RhaiResultOf<()> { - let (_arr, _map, s) = sizes; - - if s > self + if self .limits .max_string_size - .map_or(usize::MAX, NonZeroUsize::get) + .map_or(false, |max| s > max.get()) { return Err(ERR::ErrorDataTooLarge("Length of string".to_string(), pos).into()); } #[cfg(not(feature = "no_index"))] - if _arr - > self - .limits - .max_array_size - .map_or(usize::MAX, NonZeroUsize::get) + if self + .limits + .max_array_size + .map_or(false, |max| _arr > max.get()) { return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), pos).into()); } #[cfg(not(feature = "no_object"))] - if _map - > self - .limits - .max_map_size - .map_or(usize::MAX, NonZeroUsize::get) + if self + .limits + .max_map_size + .map_or(false, |max| _map > max.get()) { return Err(ERR::ErrorDataTooLarge("Size of object map".to_string(), pos).into()); } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index d6a33624..249b0319 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -496,13 +496,10 @@ impl Engine { let event = match event { Some(e) => e, - None => { - if let Some(bp) = global.debugger.is_break_point(&global.source, node) { - DebuggerEvent::BreakPoint(bp) - } else { - return Ok(None); - } - } + None => match global.debugger.is_break_point(&global.source, node) { + Some(bp) => DebuggerEvent::BreakPoint(bp), + None => return Ok(None), + }, }; self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level) diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 12e2ce5f..1144b851 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -107,10 +107,9 @@ impl GlobalRuntimeState<'_> { } else { crate::eval::DebuggerStatus::CONTINUE }, - if let Some((ref init, ..)) = engine.debugger { - init(engine) - } else { - Dynamic::UNIT + match engine.debugger { + Some((ref init, ..)) => init(engine), + None => Dynamic::UNIT, }, ), diff --git a/src/func/hashing.rs b/src/func/hashing.rs index de261de7..5f6a806c 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -77,14 +77,11 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - if let Some([seed1, seed2, seed3, seed4]) = config::AHASH_SEED { - if seed1 | seed2 | seed3 | seed4 != 0 { + match config::AHASH_SEED { + Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() - } else { - ahash::AHasher::default() } - } else { - ahash::AHasher::default() + _ => ahash::AHasher::default(), } } diff --git a/src/module/mod.rs b/src/module/mod.rs index 9e5afca5..fe48d77f 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1742,10 +1742,9 @@ impl Module { } if let Some(ref variables) = other.variables { - if let Some(ref mut m) = self.variables { - m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone()))); - } else { - self.variables = other.variables.clone(); + match self.variables { + Some(ref mut m) => m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone()))), + None => self.variables = other.variables.clone(), } } @@ -1767,10 +1766,9 @@ impl Module { self.dynamic_functions_filter += &other.dynamic_functions_filter; if let Some(ref type_iterators) = other.type_iterators { - if let Some(ref mut t) = self.type_iterators { - t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone()))); - } else { - self.type_iterators = other.type_iterators.clone(); + match self.type_iterators { + Some(ref mut t) => t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone()))), + None => self.type_iterators = other.type_iterators.clone(), } } self.all_functions = None; diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 07718c6c..d68ddfd1 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -322,10 +322,9 @@ impl FileModuleResolver { let scope = Scope::new(); - let m: Shared<_> = if let Some(global) = global { - Module::eval_ast_as_new_raw(engine, scope, global, &ast) - } else { - Module::eval_ast_as_new(scope, &ast, engine) + let m: Shared<_> = match global { + Some(global) => Module::eval_ast_as_new_raw(engine, scope, global, &ast), + None => Module::eval_ast_as_new(scope, &ast, engine), } .map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))? .into(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 79e1c130..a0f187c9 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -551,13 +551,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def } optimize_expr(&mut b.condition, state, false); - let else_stmt = if let Some(index) = def_case { - let mut def_stmt = - Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); - optimize_stmt(&mut def_stmt, state, true); - def_stmt.into() - } else { - StmtBlock::NONE + let else_stmt = match def_case { + Some(index) => { + let mut def_stmt = + Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); + optimize_stmt(&mut def_stmt, state, true); + def_stmt.into() + } + _ => StmtBlock::NONE, }; *stmt = Stmt::If( @@ -616,13 +617,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def } optimize_expr(&mut condition, state, false); - let else_stmt = if let Some(index) = def_case { - let mut def_stmt = - Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); - optimize_stmt(&mut def_stmt, state, true); - def_stmt.into() - } else { - StmtBlock::NONE + let else_stmt = match def_case { + Some(index) => { + let mut def_stmt = + Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); + optimize_stmt(&mut def_stmt, state, true); + def_stmt.into() + } + _ => StmtBlock::NONE, }; let if_stmt = @@ -665,12 +667,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // Promote the default case state.set_dirty(); - if let Some(index) = def_case { - let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); - optimize_stmt(&mut def_stmt, state, true); - *stmt = def_stmt; - } else { - *stmt = StmtBlock::empty(*pos).into(); + match def_case { + Some(index) => { + let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); + optimize_stmt(&mut def_stmt, state, true); + *stmt = def_stmt; + } + _ => *stmt = StmtBlock::empty(*pos).into(), } } // switch diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 2d25641d..3360be62 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -82,10 +82,11 @@ mod map_functions { /// print(m); // prints "#{a: 1, b: 42, c: 3, x: 0}" /// ``` pub fn set(map: &mut Map, property: &str, value: Dynamic) { - if let Some(value_ref) = map.get_mut(property) { - *value_ref = value; - } else { - map.insert(property.into(), value); + match map.get_mut(property) { + Some(value_ref) => *value_ref = value, + _ => { + map.insert(property.into(), value); + } } } /// Clear the object map. diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index cfaf3e83..72c4a6ca 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -239,10 +239,9 @@ mod string_functions { /// Clear the string, making it empty. pub fn clear(string: &mut ImmutableString) { if !string.is_empty() { - if let Some(s) = string.get_mut() { - s.clear(); - } else { - *string = ImmutableString::new(); + match string.get_mut() { + Some(s) => s.clear(), + _ => *string = ImmutableString::new(), } } } @@ -287,17 +286,20 @@ mod string_functions { /// print(text); // prints "hello" /// ``` pub fn trim(string: &mut ImmutableString) { - if let Some(s) = string.get_mut() { - let trimmed = s.trim(); + match string.get_mut() { + Some(s) => { + let trimmed = s.trim(); - if trimmed != s { - *s = trimmed.into(); + if trimmed != s { + *s = trimmed.into(); + } } - } else { - let trimmed = string.trim(); + None => { + let trimmed = string.trim(); - if trimmed != string { - *string = trimmed.into(); + if trimmed != string { + *string = trimmed.into(); + } } } } diff --git a/src/serde/deserialize.rs b/src/serde/deserialize.rs index e6c5ae3a..2534a1d5 100644 --- a/src/serde/deserialize.rs +++ b/src/serde/deserialize.rs @@ -215,10 +215,9 @@ impl<'de> Deserialize<'de> for Scope<'de> { where A: SeqAccess<'de>, { - let mut scope = if let Some(size) = access.size_hint() { - Scope::with_capacity(size) - } else { - Scope::new() + let mut scope = match access.size_hint() { + Some(size) => Scope::with_capacity(size), + None => Scope::new(), }; while let Some(ScopeEntry { diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 818b0e9e..69a280ef 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -37,10 +37,9 @@ impl Serialize for Dynamic { Union::Decimal(ref x, ..) => { use rust_decimal::prelude::ToPrimitive; - if let Some(v) = x.to_f64() { - ser.serialize_f64(v) - } else { - ser.serialize_str(&x.to_string()) + match x.to_f64() { + Some(v) => ser.serialize_f64(v), + None => ser.serialize_str(&x.to_string()), } } #[cfg(feature = "decimal")] @@ -48,10 +47,9 @@ impl Serialize for Dynamic { Union::Decimal(ref x, ..) => { use rust_decimal::prelude::ToPrimitive; - if let Some(v) = x.to_f32() { - ser.serialize_f32(v) - } else { - ser.serialize_str(&x.to_string()) + match x.to_f32() { + Some(v) => ser.serialize_f32(v), + _ => ser.serialize_str(&x.to_string()), } } From 7b7b230c5b992b7782e41c0c3f52aa7ab334b6ac Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Oct 2022 10:29:48 +0200 Subject: [PATCH 027/116] Add wasm build step to CI --- .github/workflows/build.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f847ce5..24aca3bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -110,6 +110,32 @@ jobs: with: command: build args: --manifest-path=no_std/no_std_test/Cargo.toml ${{matrix.flags}} + wasm: + name: Check Wasm build + runs-on: ubuntu-latest + strategy: + matrix: + flags: + - "" + - "--no-default-features" + - "--features wasm-bindgen" + - "--no-default-features --features wasm-bindgen" + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_MSRV }} + override: true + target: wasm32-unknown-unknown + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --target wasm32-unknown-unknown ${{matrix.flags}} + rustfmt: name: Check Formatting runs-on: windows-latest From 0c4564f597bc64143c9794a4e024e2584c1f1add Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Oct 2022 12:06:22 +0200 Subject: [PATCH 028/116] Comment out failing tests - just doc what passes currently --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24aca3bb..da5f1126 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,9 +116,10 @@ jobs: strategy: matrix: flags: - - "" +# These fail currently, future PR should fix them +# - "" +# - "--features wasm-bindgen" - "--no-default-features" - - "--features wasm-bindgen" - "--no-default-features --features wasm-bindgen" fail-fast: false steps: From 33d3e250a8122b519183de956d25c04f57766e9d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 12 Oct 2022 08:13:27 +0800 Subject: [PATCH 029/116] Improve wasm builds. --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1034f923..fe415e8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"] [dependencies] smallvec = { version = "1.7", default-features = false, features = ["union", "const_new", "const_generics"] } -ahash = { version = "0.8", default-features = false } +ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } num-traits = { version = "0.2", default-features = false } bitflags = { version = "1", default-features = false } smartstring = { version = "1", default-features = false } @@ -40,8 +40,8 @@ serde_bytes = "0.11" serde_json = { version = "1.0", default-features = false, features = ["alloc"] } [features] -default = ["std"] -std = ["ahash/std", "ahash/runtime-rng", "num-traits/std", "smartstring/std"] +default = ["std", "ahash/runtime-rng"] # ahash/runtime-rng trumps ahash/compile-time-rng +std = ["ahash/std", "num-traits/std", "smartstring/std"] unchecked = [] # unchecked arithmetic sync = [] # restrict to only types that implement Send + Sync no_position = [] # do not track position in the parser @@ -64,7 +64,7 @@ debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std -no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng", "hashbrown/ahash-compile-time-rng"] +no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm"] # compiling for WASM wasm-bindgen = ["instant/wasm-bindgen"] From a581d51e7ad8061d8f948a4913fb1d2938446691 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 12 Oct 2022 09:44:46 +0800 Subject: [PATCH 030/116] Fix no-std. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fe415e8b..5b506221 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std -no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm"] +no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown"] # compiling for WASM wasm-bindgen = ["instant/wasm-bindgen"] From e66b415de25ec96ccd42f94a66099d529cd0977b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 12 Oct 2022 14:49:05 +0800 Subject: [PATCH 031/116] Add Wasi CI. --- .github/workflows/build.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da5f1126..58bca5de 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: check + # typical build with various feature combinations build: name: Build @@ -86,6 +87,7 @@ jobs: with: command: test args: ${{matrix.flags}} + # no-std builds are a bit more extensive to test no_std_build: name: NoStdBuild @@ -110,32 +112,38 @@ jobs: with: command: build args: --manifest-path=no_std/no_std_test/Cargo.toml ${{matrix.flags}} + wasm: name: Check Wasm build runs-on: ubuntu-latest strategy: matrix: flags: + - "--target wasm32-wasi" # These fail currently, future PR should fix them -# - "" -# - "--features wasm-bindgen" - - "--no-default-features" - - "--no-default-features --features wasm-bindgen" +# - "--target wasm32-unknown-unknown" +# - "--target wasm32-unknown-unknown --features wasm-bindgen" + - "--target wasm32-unknown-unknown --no-default-features" + - "--target wasm32-unknown-unknown --no-default-features --features wasm-bindgen" fail-fast: false steps: - name: Checkout uses: actions/checkout@v2 - - name: Setup Toolchain + - name: Setup Wasm Toolchain uses: actions-rs/toolchain@v1 with: - toolchain: ${{ env.RUST_MSRV }} override: true target: wasm32-unknown-unknown + - name: Setup Wasi Toolchain + uses: actions-rs/toolchain@v1 + with: + override: true + target: wasm32-wasi - name: Build uses: actions-rs/cargo@v1 with: command: build - args: --target wasm32-unknown-unknown ${{matrix.flags}} + args: ${{matrix.flags}} rustfmt: name: Check Formatting @@ -160,6 +168,7 @@ jobs: with: command: clippy args: --all -- -Aclippy::all -Dclippy::perf + codegen_build: name: Codegen Build runs-on: ${{matrix.os}} From e2699aa058099c7b180d22c6831d608ef7dce94a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 12 Oct 2022 15:14:10 +0800 Subject: [PATCH 032/116] Fix wasm toolchain. --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58bca5de..7a72d1d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -129,14 +129,16 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Setup Wasm Toolchain + - name: Setup Generic Wasm Toolchain uses: actions-rs/toolchain@v1 with: + toolchain: stable override: true target: wasm32-unknown-unknown - name: Setup Wasi Toolchain uses: actions-rs/toolchain@v1 with: + toolchain: stable override: true target: wasm32-wasi - name: Build From 31d045279ff4fdf0111b11eb15484294536c7f00 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 11:57:36 +0800 Subject: [PATCH 033/116] Fix imports in eval. --- CHANGELOG.md | 11 ++++++----- src/func/call.rs | 4 +++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c2848dd..2873c736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Rhai Release Notes Version 1.11.0 ============== +Bug fixes +--------- + +* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications. +* `import` statements inside `eval` no longer cause errors in subsequent code. + New features ------------ @@ -12,11 +18,6 @@ New features * It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries. * A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. -Bug fixes ---------- - -* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications. - Enhancements ------------ diff --git a/src/func/call.rs b/src/func/call.rs index c093c237..ce83786f 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1137,6 +1137,7 @@ impl Engine { KEYWORD_EVAL if total_args == 1 => { // eval - only in function call style let orig_scope_len = scope.len(); + let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); let (arg_value, pos) = self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; @@ -1155,7 +1156,8 @@ impl Engine { // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. - if scope.len() != orig_scope_len { + // The same is true for imports. + if scope.len() != orig_scope_len || global.num_imports() != orig_imports_len { global.always_search_scope = true; } From b65b7b05a51e70585b1c6eb5b2de65fe16572b80 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 12:14:28 +0800 Subject: [PATCH 034/116] Allow global functions in non-aliased imports. --- CHANGELOG.md | 1 + src/eval/stmt.rs | 22 +++++++++++++--------- src/parser.rs | 28 ++++++++++++++-------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2873c736..00b1d8e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Bug fixes * `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications. * `import` statements inside `eval` no longer cause errors in subsequent code. +* Functions marked `global` in `import`ed modules with no alias names now work properly. New features ------------ diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 726b661f..77e39dea 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -952,15 +952,19 @@ impl Engine { }); if let Ok(module) = module_result { - if !export.is_empty() { - if module.is_indexed() { - global.push_import(export.name.clone(), module); - } else { - // Index the module (making a clone copy if necessary) if it is not indexed - let mut m = crate::func::shared_take_or_clone(module); - m.build_index(); - global.push_import(export.name.clone(), m); - } + let (export, must_be_indexed) = if !export.is_empty() { + (export.name.clone(), true) + } else { + (self.get_interned_string(""), false) + }; + + if !must_be_indexed || module.is_indexed() { + global.push_import(export, module); + } else { + // Index the module (making a clone copy if necessary) if it is not indexed + let mut m = crate::func::shared_take_or_clone(module); + m.build_index(); + global.push_import(export, m); } global.num_modules_loaded += 1; diff --git a/src/parser.rs b/src/parser.rs index 798067c9..23f96d7b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2993,24 +2993,24 @@ impl Engine { // import expr ... let expr = self.parse_expr(input, state, lib, settings.level_up())?; - // import expr; - if !match_token(input, Token::As).0 { - let empty = Ident { + let export = if !match_token(input, Token::As).0 { + // import expr; + Ident { name: state.get_interned_string(""), pos: Position::NONE, - }; - return Ok(Stmt::Import((expr, empty).into(), settings.pos)); - } + } + } else { + // import expr as name ... + let (name, pos) = parse_var_name(input)?; + Ident { + name: state.get_interned_string(name), + pos, + } + }; - // import expr as name ... - let (name, pos) = parse_var_name(input)?; - let name = state.get_interned_string(name); - state.imports.push(name.clone()); + state.imports.push(export.name.clone()); - Ok(Stmt::Import( - (expr, Ident { name, pos }).into(), - settings.pos, - )) + Ok(Stmt::Import((expr, export).into(), settings.pos)) } /// Parse an export statement. From 3d0626c76fdecb16519147b1723684732429422b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 12:23:04 +0800 Subject: [PATCH 035/116] Streamline global state. --- src/eval/global_state.rs | 72 ++++++++++++++++++---------------------- src/func/native.rs | 2 +- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 1144b851..98e39870 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -23,12 +23,9 @@ pub type GlobalConstants = // corresponds to that key. #[derive(Clone)] pub struct GlobalRuntimeState<'a> { - /// Stack of module names. - #[cfg(not(feature = "no_module"))] - keys: crate::StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec>, + modules: crate::StaticVec<(crate::ImmutableString, crate::Shared)>, /// Source of the current context. /// /// No source if the string is empty. @@ -80,8 +77,6 @@ impl GlobalRuntimeState<'_> { #[must_use] pub fn new(engine: &Engine) -> Self { Self { - #[cfg(not(feature = "no_module"))] - keys: crate::StaticVec::new_const(), #[cfg(not(feature = "no_module"))] modules: crate::StaticVec::new_const(), source: Identifier::new_const(), @@ -123,7 +118,7 @@ impl GlobalRuntimeState<'_> { #[inline(always)] #[must_use] pub fn num_imports(&self) -> usize { - self.keys.len() + self.modules.len() } /// Get the globally-imported [module][crate::Module] at a particular index. /// @@ -132,7 +127,7 @@ impl GlobalRuntimeState<'_> { #[inline(always)] #[must_use] pub fn get_shared_import(&self, index: usize) -> Option> { - self.modules.get(index).cloned() + self.modules.get(index).map(|(_, m)| m).cloned() } /// Get a mutable reference to the globally-imported [module][crate::Module] at a /// particular index. @@ -146,7 +141,7 @@ impl GlobalRuntimeState<'_> { &mut self, index: usize, ) -> Option<&mut crate::Shared> { - self.modules.get_mut(index) + self.modules.get_mut(index).map(|(_, m)| m) } /// Get the index of a globally-imported [module][crate::Module] by name. /// @@ -155,12 +150,12 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn find_import(&self, name: &str) -> Option { - let len = self.keys.len(); + let len = self.modules.len(); - self.keys + self.modules .iter() .rev() - .position(|key| key.as_str() == name) + .position(|(key, _)| key.as_str() == name) .map(|i| len - 1 - i) } /// Push an imported [module][crate::Module] onto the stack. @@ -173,8 +168,7 @@ impl GlobalRuntimeState<'_> { name: impl Into, module: impl Into>, ) { - self.keys.push(name.into()); - self.modules.push(module.into()); + self.modules.push((name.into(), module.into())); } /// Truncate the stack of globally-imported [modules][crate::Module] to a particular length. /// @@ -182,7 +176,6 @@ impl GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn truncate_imports(&mut self, size: usize) { - self.keys.truncate(size); self.modules.truncate(size); } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. @@ -192,10 +185,9 @@ impl GlobalRuntimeState<'_> { #[allow(dead_code)] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.keys + self.modules .iter() .rev() - .zip(self.modules.iter().rev()) .map(|(name, module)| (name.as_str(), &**module)) } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. @@ -206,8 +198,8 @@ impl GlobalRuntimeState<'_> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { - self.keys.iter().rev().zip(self.modules.iter().rev()) + ) -> impl Iterator)> { + self.modules.iter().rev() } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order. /// @@ -217,8 +209,8 @@ impl GlobalRuntimeState<'_> { #[inline] pub fn scan_imports_raw( &self, - ) -> impl Iterator)> { - self.keys.iter().zip(self.modules.iter()) + ) -> impl Iterator)> { + self.modules.iter() } /// Does the specified function hash key exist in the stack of globally-imported /// [modules][crate::Module]? @@ -229,7 +221,9 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn contains_qualified_fn(&self, hash: u64) -> bool { - self.modules.iter().any(|m| m.contains_qualified_fn(hash)) + self.modules + .iter() + .any(|(_, m)| m.contains_qualified_fn(hash)) } /// Get the specified function via its hash key from the stack of globally-imported /// [modules][crate::Module]. @@ -245,7 +239,7 @@ impl GlobalRuntimeState<'_> { 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()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of /// globally-imported [modules][crate::Module]? @@ -256,7 +250,9 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn contains_iter(&self, id: std::any::TypeId) -> bool { - self.modules.iter().any(|m| m.contains_qualified_iter(id)) + self.modules + .iter() + .any(|(_, m)| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported /// [modules][crate::Module]. @@ -269,7 +265,7 @@ impl GlobalRuntimeState<'_> { self.modules .iter() .rev() - .find_map(|m| m.get_qualified_iter(id)) + .find_map(|(_, m)| m.get_qualified_iter(id)) } /// Get the current source. #[inline] @@ -312,29 +308,26 @@ impl GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] impl IntoIterator for GlobalRuntimeState<'_> { type Item = (crate::ImmutableString, crate::Shared); - type IntoIter = std::iter::Zip< - std::iter::Rev>, - std::iter::Rev; 3]>>, + type IntoIter = std::iter::Rev< + smallvec::IntoIter< + [(crate::ImmutableString, crate::Shared); crate::STATIC_VEC_INLINE_SIZE], + >, >; fn into_iter(self) -> Self::IntoIter { - self.keys - .into_iter() - .rev() - .zip(self.modules.into_iter().rev()) + self.modules.into_iter().rev() } } #[cfg(not(feature = "no_module"))] impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { - type Item = (&'a crate::ImmutableString, &'a crate::Shared); - type IntoIter = std::iter::Zip< - std::iter::Rev>, - std::iter::Rev>>, + type Item = &'a (crate::ImmutableString, crate::Shared); + type IntoIter = std::iter::Rev< + std::slice::Iter<'a, (crate::ImmutableString, crate::Shared)>, >; fn into_iter(self) -> Self::IntoIter { - self.keys.iter().rev().zip(self.modules.iter().rev()) + self.modules.iter().rev() } } @@ -345,8 +338,7 @@ impl, M: Into>> Ext #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { - self.keys.push(k.into()); - self.modules.push(m.into()); + self.modules.push((k.into(), m.into())); } } } @@ -358,7 +350,7 @@ impl fmt::Debug for GlobalRuntimeState<'_> { let mut f = f.debug_struct("GlobalRuntimeState"); #[cfg(not(feature = "no_module"))] - f.field("imports", &self.keys.iter().zip(self.modules.iter())); + f.field("imports", &self.modules); f.field("source", &self.source) .field("num_operations", &self.num_operations); diff --git a/src/func/native.rs b/src/func/native.rs index f421c262..490fba30 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -235,7 +235,7 @@ impl<'a> NativeCallContext<'a> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator)> { self.global.iter().flat_map(|&g| g.iter_imports_raw()) } /// _(internals)_ The current [`GlobalRuntimeState`], if any. From 38e47c909d53dd1d0c1048e68c96e664c43b739f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 14:04:54 +0800 Subject: [PATCH 036/116] Streamline code. --- src/ast/expr.rs | 3 +++ src/ast/stmt.rs | 10 +++++++--- src/eval/expr.rs | 2 +- src/lib.rs | 13 ++++++++++--- src/module/resolvers/collection.rs | 13 ++++++++----- src/types/interner.rs | 2 +- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 76c9587d..b3b55aac 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -493,6 +493,9 @@ impl fmt::Debug for Expr { } } f.write_str(&x.3)?; + if let Some(n) = x.1.index() { + write!(f, " #{}", n)?; + } if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { write!(f, " #{}", n)?; } diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index a763860c..a72e2f01 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -302,6 +302,10 @@ pub struct TryCatchBlock { pub catch_block: StmtBlock, } +/// Number of items to keep inline for [`StmtBlockContainer`]. +#[cfg(not(feature = "no_std"))] +const STMT_BLOCK_INLINE_SIZE: usize = 8; + /// _(internals)_ The underlying container type for [`StmtBlock`]. /// Exported under the `internals` feature only. /// @@ -309,7 +313,7 @@ pub struct TryCatchBlock { /// hold a statements block, with the assumption that most program blocks would container fewer than /// 8 statements, and those that do have a lot more statements. #[cfg(not(feature = "no_std"))] -pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; 8]>; +pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; STMT_BLOCK_INLINE_SIZE]>; /// _(internals)_ The underlying container type for [`StmtBlock`]. /// Exported under the `internals` feature only. @@ -491,9 +495,9 @@ impl From for StmtBlock { impl IntoIterator for StmtBlock { type Item = Stmt; #[cfg(not(feature = "no_std"))] - type IntoIter = smallvec::IntoIter<[Stmt; 8]>; + type IntoIter = smallvec::IntoIter<[Stmt; STMT_BLOCK_INLINE_SIZE]>; #[cfg(feature = "no_std")] - type IntoIter = smallvec::IntoIter<[Stmt; 3]>; + type IntoIter = smallvec::IntoIter<[Stmt; crate::STATIC_VEC_INLINE_SIZE]>; #[inline(always)] fn into_iter(self) -> Self::IntoIter { diff --git a/src/eval/expr.rs b/src/eval/expr.rs index ce34a8ad..72915161 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -24,13 +24,13 @@ impl Engine { let root = namespace.root(); - // Qualified - check if the root module is directly indexed let index = if global.always_search_scope { None } else { namespace.index() }; + // Qualified - check if the root module is directly indexed if let Some(index) = index { let offset = global.num_imports() - index.get(); diff --git a/src/lib.rs b/src/lib.rs index 4976d4b7..7b36c9cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -341,6 +341,9 @@ pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeS #[cfg(feature = "metadata")] pub use api::definitions::Definitions; +/// Number of items to keep inline for [`StaticVec`]. +const STATIC_VEC_INLINE_SIZE: usize = 3; + /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a /// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. /// @@ -373,7 +376,7 @@ pub use api::definitions::Definitions; /// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels /// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get). #[cfg(not(feature = "internals"))] -type StaticVec = smallvec::SmallVec<[T; 3]>; +type StaticVec = smallvec::SmallVec<[T; STATIC_VEC_INLINE_SIZE]>; /// _(internals)_ Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), /// which is a [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. @@ -408,7 +411,11 @@ type StaticVec = smallvec::SmallVec<[T; 3]>; /// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels /// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get). #[cfg(feature = "internals")] -pub type StaticVec = smallvec::SmallVec<[T; 3]>; +pub type StaticVec = smallvec::SmallVec<[T; STATIC_VEC_INLINE_SIZE]>; + +/// Number of items to keep inline for [`FnArgsVec`]. +#[cfg(not(feature = "no_closure"))] +const FN_ARGS_VEC_INLINE_SIZE: usize = 5; /// Inline arguments storage for function calls. /// @@ -423,7 +430,7 @@ pub type StaticVec = smallvec::SmallVec<[T; 3]>; /// /// Under `no_closure`, this type aliases to [`StaticVec`][crate::StaticVec] instead. #[cfg(not(feature = "no_closure"))] -type FnArgsVec = smallvec::SmallVec<[T; 5]>; +type FnArgsVec = smallvec::SmallVec<[T; FN_ARGS_VEC_INLINE_SIZE]>; /// Inline arguments storage for function calls. /// This type aliases to [`StaticVec`][crate::StaticVec]. diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index fd6780d4..8a61d715 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,7 +1,10 @@ -use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR}; +use crate::{ + Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, StaticVec, ERR, + STATIC_VEC_INLINE_SIZE, +}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{ops::AddAssign, slice::Iter, vec::IntoIter}; +use std::{ops::AddAssign, slice::Iter}; /// [Module] resolution service that holds a collection of module resolvers, /// to be searched in sequential order. @@ -21,7 +24,7 @@ use std::{ops::AddAssign, slice::Iter, vec::IntoIter}; /// engine.set_module_resolver(collection); /// ``` #[derive(Default)] -pub struct ModuleResolversCollection(Vec>); +pub struct ModuleResolversCollection(StaticVec>); impl ModuleResolversCollection { /// Create a new [`ModuleResolversCollection`]. @@ -43,7 +46,7 @@ impl ModuleResolversCollection { #[inline(always)] #[must_use] pub const fn new() -> Self { - Self(Vec::new()) + Self(StaticVec::new_const()) } /// Append a [module resolver][ModuleResolver] to the end. #[inline(always)] @@ -109,7 +112,7 @@ impl ModuleResolversCollection { impl IntoIterator for ModuleResolversCollection { type Item = Box; - type IntoIter = IntoIter>; + type IntoIter = smallvec::IntoIter<[Box; STATIC_VEC_INLINE_SIZE]>; #[inline(always)] #[must_use] diff --git a/src/types/interner.rs b/src/types/interner.rs index 5832dbe0..4b7d387e 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -110,7 +110,7 @@ impl StringsInterner<'_> { // If the interner is over capacity, remove the longest entry that has the lowest count if self.cache.len() > self.capacity { - // Leave some buffer to grow when shrinking the cache. + // Throttle: leave some buffer to grow when shrinking the cache. // We leave at least two entries, one for the empty string, and one for the string // that has just been inserted. let max = if self.capacity < 5 { From 71e475be1372f7e448e8ebc344a082c7c49d0508 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 15:23:58 +0800 Subject: [PATCH 037/116] Fix no_module build. --- src/ast/expr.rs | 1 + src/func/call.rs | 20 +++++++++----------- src/func/callable_function.rs | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index b3b55aac..fed8d861 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -493,6 +493,7 @@ impl fmt::Debug for Expr { } } f.write_str(&x.3)?; + #[cfg(not(feature = "no_module"))] if let Some(n) = x.1.index() { write!(f, " #{}", n)?; } diff --git a/src/func/call.rs b/src/func/call.rs index ce83786f..64d8958c 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1137,27 +1137,25 @@ impl Engine { KEYWORD_EVAL if total_args == 1 => { // eval - only in function call style let orig_scope_len = scope.len(); + #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); let (arg_value, pos) = self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; - let script = &arg_value + let s = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; - let result = self.eval_script_expr_in_place( - scope, - global, - caches, - lib, - script, - pos, - level + 1, - ); + let result = + self.eval_script_expr_in_place(scope, global, caches, lib, s, pos, level + 1); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. // The same is true for imports. - if scope.len() != orig_scope_len || global.num_imports() != orig_imports_len { + let scope_changed = scope.len() != orig_scope_len; + #[cfg(not(feature = "no_module"))] + let scope_changed = scope_changed || global.num_imports() != orig_imports_len; + + if scope_changed { global.always_search_scope = true; } diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index 3973b1b1..e6b85a29 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -199,7 +199,7 @@ impl CallableFunction { Self::Script(..) => None, } } - /// Create a new [`CallableFunction::Method`] from a [built-in function][`FnBuiltin`]. + /// Create a new [`CallableFunction::Method`] from a built-in function. #[inline(always)] #[must_use] pub fn from_fn_builtin(func: FnBuiltin) -> Self { From ea63c66cf01fa6ce0159328c59609df2902f5f21 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 16:57:14 +0800 Subject: [PATCH 038/116] Add call_native_fn for context. --- CHANGELOG.md | 35 +++++++++--- src/api/call_fn.rs | 1 + src/func/native.rs | 101 ++++++++++++++++++++++++++++++++--- src/packages/array_basic.rs | 6 +-- src/packages/map_basic.rs | 2 +- src/packages/string_basic.rs | 2 +- 6 files changed, 128 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00b1d8e2..ffcc3516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,23 +11,44 @@ Bug fixes * `import` statements inside `eval` no longer cause errors in subsequent code. * Functions marked `global` in `import`ed modules with no alias names now work properly. +Speed Improvements +------------------ + +* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. + New features ------------ ### Stable hashing -* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries. -* A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. +* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. +* This is necessary when using Rhai across shared-library boundaries. +* A build script is used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. + +### Serializable `Scope` + +* `Scope` is now serializable and deserializable via `serde`. + +### Call native Rust functions in `NativeCallContext` + +* `NativeCallContext::call_native_fn` is added to call registered native Rust functions only. +* `NativeCallContext::call_native_fn_raw` is added as the advanced version. +* This is often desirable as Rust functions typically do not want a similar-named scripted function to hijack the process -- which will cause brittleness. + +### Custom syntax improvements + +* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). +* This facilitates more accurate parsing by separating strings and identifiers. + +### Limits API + +* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked`. +* This helps avoid the proliferation of unnecessary feature flags in third-party library code. Enhancements ------------ -* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers. -* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. -* `Scope` is now serializable and deserializable via `serde`. -* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline. * `parse_json` function is added to parse a JSON string into an object map. -* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code. Version 1.10.1 diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index ac1fb7f3..3b0a7615 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -233,6 +233,7 @@ impl Engine { arg_values, ) } + /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. fn _call_fn( &self, diff --git a/src/func/native.rs b/src/func/native.rs index 490fba30..34cd6ec3 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -135,7 +135,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> impl<'a> NativeCallContext<'a> { /// _(internals)_ Create a new [`NativeCallContext`]. - /// Exported under the `metadata` feature only. + /// Exported under the `internals` feature only. #[deprecated( since = "1.3.0", note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly." @@ -274,7 +274,7 @@ impl<'a> NativeCallContext<'a> { let mut args: StaticVec<_> = arg_values.iter_mut().collect(); - let result = self.call_fn_raw(fn_name, false, false, &mut args)?; + let result = self._call_fn_raw(fn_name, false, false, false, &mut args)?; let typ = self.engine().map_type_name(result.type_name()); @@ -283,7 +283,32 @@ impl<'a> NativeCallContext<'a> { ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into() }) } - /// Call a function inside the call context. + /// Call a registered native Rust function inside the call context with the provided arguments. + /// + /// This is often useful because Rust functions typically only want to cross-call other + /// registered Rust functions and not have to worry about scripted functions hijacking the + /// process unknowingly (or deliberately). + #[inline] + pub fn call_native_fn( + &self, + fn_name: impl AsRef, + args: impl FuncArgs, + ) -> RhaiResultOf { + let mut arg_values = StaticVec::new_const(); + args.parse(&mut arg_values); + + let mut args: StaticVec<_> = arg_values.iter_mut().collect(); + + let result = self._call_fn_raw(fn_name, true, false, false, &mut args)?; + + let typ = self.engine().map_type_name(result.type_name()); + + result.try_cast().ok_or_else(|| { + let t = self.engine().map_type_name(type_name::()).into(); + ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into() + }) + } + /// Call a function (native Rust or scripted) inside the call context. /// /// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for /// a script-defined function (or the object of a method call). @@ -302,6 +327,7 @@ impl<'a> NativeCallContext<'a> { /// /// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is /// not consumed. + #[inline(always)] pub fn call_fn_raw( &self, fn_name: impl AsRef, @@ -309,15 +335,76 @@ impl<'a> NativeCallContext<'a> { is_method_call: bool, args: &mut [&mut Dynamic], ) -> RhaiResult { - let mut global = self + self._call_fn_raw(fn_name, false, is_ref_mut, is_method_call, args) + } + /// Call a registered native Rust function inside the call context. + /// + /// This is often useful because Rust functions typically only want to cross-call other + /// registered Rust functions and not have to worry about scripted functions hijacking the + /// process unknowingly (or deliberately). + /// + /// # WARNING - Low Level API + /// + /// This function is very low level. + /// + /// # Arguments + /// + /// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid + /// unnecessarily cloning the arguments. + /// + /// **DO NOT** reuse the arguments after this call. If they are needed afterwards, clone them + /// _before_ calling this function. + /// + /// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is + /// not consumed. + #[inline(always)] + pub fn call_native_fn_raw( + &self, + fn_name: impl AsRef, + is_ref_mut: bool, + args: &mut [&mut Dynamic], + ) -> RhaiResult { + self._call_fn_raw(fn_name, true, is_ref_mut, false, args) + } + + /// Call a function (native Rust or scripted) inside the call context. + fn _call_fn_raw( + &self, + fn_name: impl AsRef, + native_only: bool, + is_ref_mut: bool, + is_method_call: bool, + args: &mut [&mut Dynamic], + ) -> RhaiResult { + let global = &mut self .global .cloned() .unwrap_or_else(|| GlobalRuntimeState::new(self.engine())); - let mut caches = Caches::new(); + let caches = &mut Caches::new(); let fn_name = fn_name.as_ref(); let args_len = args.len(); + if native_only { + return self + .engine() + .call_native_fn( + global, + caches, + self.lib, + fn_name, + calc_fn_hash(None, fn_name, args_len), + args, + is_ref_mut, + false, + Position::NONE, + self.level + 1, + ) + .map(|(r, ..)| r); + } + + // Native or script + let hash = if is_method_call { FnCallHashes::from_all( #[cfg(not(feature = "no_function"))] @@ -331,8 +418,8 @@ impl<'a> NativeCallContext<'a> { self.engine() .exec_fn_call( None, - &mut global, - &mut caches, + global, + caches, self.lib, fn_name, hash, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 7e639275..2c5bd716 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -835,7 +835,7 @@ pub mod array_functions { for item in array { if ctx - .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) + .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()]) .or_else(|err| match *err { ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => { if item.type_id() == value.type_id() { @@ -927,7 +927,7 @@ pub mod array_functions { for (i, item) in array.iter_mut().enumerate().skip(start) { if ctx - .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) + .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()]) .or_else(|err| match *err { ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => { if item.type_id() == value.type_id() { @@ -2313,7 +2313,7 @@ pub mod array_functions { for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) { if !ctx - .call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2]) + .call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2]) .or_else(|err| match *err { ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => { if a1.type_id() == a2.type_id() { diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 3360be62..e5174c1b 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -213,7 +213,7 @@ mod map_functions { for (m1, v1) in map1 { if let Some(v2) = map2.get_mut(m1) { let equals = ctx - .call_fn_raw(OP_EQUALS, true, false, &mut [v1, v2])? + .call_native_fn_raw(OP_EQUALS, true, &mut [v1, v2])? .as_bool() .unwrap_or(false); diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index d8c3845a..a0763ada 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -38,7 +38,7 @@ pub fn print_with_func( ctx: &NativeCallContext, value: &mut Dynamic, ) -> crate::ImmutableString { - match ctx.call_fn_raw(fn_name, true, false, &mut [value]) { + match ctx.call_native_fn_raw(fn_name, true, &mut [value]) { Ok(result) if result.is::() => { result.into_immutable_string().expect("`ImmutableString`") } From ac05f0a0a8886051c0ededf2ad23cbe548105dc1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 18:31:40 +0800 Subject: [PATCH 039/116] Check if function calls cannot be scripted. --- src/ast/expr.rs | 11 +++- src/eval/expr.rs | 11 +++- src/func/call.rs | 152 ++++++++++++++++++++++++--------------------- src/func/native.rs | 1 + src/optimizer.rs | 29 ++++----- src/parser.rs | 40 +++++++++--- 6 files changed, 142 insertions(+), 102 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index fed8d861..4adaaecf 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -181,7 +181,7 @@ impl FnCallHashes { /// _(internals)_ A function call. /// Exported under the `internals` feature only. -#[derive(Clone, Default, Hash)] +#[derive(Clone, Hash)] pub struct FnCallExpr { /// Namespace of the function, if any. #[cfg(not(feature = "no_module"))] @@ -196,6 +196,9 @@ pub struct FnCallExpr { pub capture_parent_scope: bool, /// Is this function call a native operator? pub operator_token: Option, + /// Can this function call be a scripted function? + #[cfg(not(feature = "no_function"))] + pub can_be_script: bool, /// [Position] of the function name. pub pos: Position, } @@ -215,6 +218,10 @@ impl fmt::Debug for FnCallExpr { if let Some(ref token) = self.operator_token { ff.field("operator_token", token); } + #[cfg(not(feature = "no_function"))] + if self.can_be_script { + ff.field("can_be_script", &self.can_be_script); + } ff.field("hash", &self.hashes) .field("name", &self.name) .field("args", &self.args); @@ -684,6 +691,8 @@ impl Expr { args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, operator_token: None, + #[cfg(not(feature = "no_function"))] + can_be_script: true, pos, } .into(), diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 72915161..c09b48a3 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -223,9 +223,16 @@ impl Engine { hashes, args, operator_token, + #[cfg(not(feature = "no_function"))] + can_be_script, .. } = expr; + #[cfg(not(feature = "no_function"))] + let native = !can_be_script; + #[cfg(feature = "no_function")] + let native = true; + // Short-circuit native binary operator call if under Fast Operators mode if operator_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self @@ -251,7 +258,8 @@ impl Engine { return self .exec_fn_call( - None, global, caches, lib, name, *hashes, operands, false, false, pos, level, + None, global, caches, lib, name, native, *hashes, operands, false, false, pos, + level, ) .map(|(v, ..)| v); } @@ -280,6 +288,7 @@ impl Engine { lib, this_ptr, name, + native, first_arg, args, *hashes, diff --git a/src/func/call.rs b/src/func/call.rs index 64d8958c..c3ca7511 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -578,6 +578,7 @@ impl Engine { caches: &mut Caches, lib: &[&Module], fn_name: &str, + _native_only: bool, hashes: FnCallHashes, args: &mut FnCallArgs, is_ref_mut: bool, @@ -644,89 +645,90 @@ impl Engine { let level = level + 1; - // Script-defined function call? #[cfg(not(feature = "no_function"))] - let local_entry = &mut None; + if !_native_only { + // Script-defined function call? + let local_entry = &mut None; - #[cfg(not(feature = "no_function"))] - if let Some(FnResolutionCacheEntry { func, ref source }) = self - .resolve_fn( - global, - caches, - local_entry, - lib, - fn_name, - hashes.script, - None, - false, - None, - ) - .cloned() - { - // Script function call - assert!(func.is_script()); - - let func = func.get_script_fn_def().expect("script-defined function"); - - if func.body.is_empty() { - return Ok((Dynamic::UNIT, false)); - } - - let mut empty_scope; - let scope = match _scope { - Some(scope) => scope, - None => { - empty_scope = Scope::new(); - &mut empty_scope - } - }; - - let orig_source = mem::replace( - &mut global.source, - source - .as_ref() - .map_or(crate::Identifier::new_const(), |s| (**s).clone()), - ); - - let result = if _is_method_call { - // Method call of script function - map first argument to `this` - let (first_arg, rest_args) = args.split_first_mut().unwrap(); - - self.call_script_fn( - scope, + if let Some(FnResolutionCacheEntry { func, ref source }) = self + .resolve_fn( global, caches, + local_entry, lib, - &mut Some(*first_arg), - func, - rest_args, - true, - pos, - level, + fn_name, + hashes.script, + None, + false, + None, ) - } else { - // Normal call of script function - let mut backup = ArgBackup::new(); + .cloned() + { + // Script function call + assert!(func.is_script()); - // The first argument is a reference? - if is_ref_mut && !args.is_empty() { - backup.change_first_arg_to_copy(args); + let func = func.get_script_fn_def().expect("script-defined function"); + + if func.body.is_empty() { + return Ok((Dynamic::UNIT, false)); } - let result = self.call_script_fn( - scope, global, caches, lib, &mut None, func, args, true, pos, level, + let mut empty_scope; + let scope = match _scope { + Some(scope) => scope, + None => { + empty_scope = Scope::new(); + &mut empty_scope + } + }; + + let orig_source = mem::replace( + &mut global.source, + source + .as_ref() + .map_or(crate::Identifier::new_const(), |s| (**s).clone()), ); - // Restore the original reference - backup.restore_first_arg(args); + let result = if _is_method_call { + // Method call of script function - map first argument to `this` + let (first_arg, rest_args) = args.split_first_mut().unwrap(); - result - }; + self.call_script_fn( + scope, + global, + caches, + lib, + &mut Some(*first_arg), + func, + rest_args, + true, + pos, + level, + ) + } else { + // Normal call of script function + let mut backup = ArgBackup::new(); - // Restore the original source - global.source = orig_source; + // The first argument is a reference? + if is_ref_mut && !args.is_empty() { + backup.change_first_arg_to_copy(args); + } - return Ok((result?, false)); + let result = self.call_script_fn( + scope, global, caches, lib, &mut None, func, args, true, pos, level, + ); + + // Restore the original reference + backup.restore_first_arg(args); + + result + }; + + // Restore the original source + global.source = orig_source; + + return Ok((result?, false)); + } } // Native function call @@ -836,6 +838,7 @@ impl Engine { caches, lib, fn_name, + false, new_hash, &mut args, false, @@ -881,6 +884,7 @@ impl Engine { caches, lib, fn_name, + false, new_hash, &mut args, is_ref_mut, @@ -968,6 +972,7 @@ impl Engine { caches, lib, fn_name, + false, hash, &mut args, is_ref_mut, @@ -995,6 +1000,7 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, + native_only: bool, first_arg: Option<&Expr>, args_expr: &[Expr], hashes: FnCallHashes, @@ -1003,6 +1009,7 @@ impl Engine { pos: Position, level: usize, ) -> RhaiResult { + let native = native_only; let mut first_arg = first_arg; let mut a_expr = args_expr; let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len(); @@ -1199,8 +1206,8 @@ impl Engine { return self .exec_fn_call( - scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, - level, + scope, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, + pos, level, ) .map(|(v, ..)| v); } @@ -1262,7 +1269,8 @@ impl Engine { } self.exec_fn_call( - None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, + None, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, pos, + level, ) .map(|(v, ..)| v) } diff --git a/src/func/native.rs b/src/func/native.rs index 34cd6ec3..b3681e74 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -422,6 +422,7 @@ impl<'a> NativeCallContext<'a> { caches, self.lib, fn_name, + false, hash, args, is_ref_mut, diff --git a/src/optimizer.rs b/src/optimizer.rs index a0f187c9..dea68ea3 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -897,24 +897,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b Stmt::Expr(expr) => { optimize_expr(expr, state, false); - match &mut **expr { - // func(...) - Expr::FnCall(x, pos) => { - state.set_dirty(); - *stmt = Stmt::FnCall(mem::take(x), *pos); - } - // {...}; - Expr::Stmt(x) => { - if x.is_empty() { - state.set_dirty(); - *stmt = Stmt::Noop(x.position()); - } else { - state.set_dirty(); - *stmt = mem::take(&mut **x).into(); - } - } - // expr; - _ => (), + if matches!(**expr, Expr::FnCall(..) | Expr::Stmt(..)) { + state.set_dirty(); + *stmt = match *mem::take(expr) { + // func(...); + Expr::FnCall(x, pos) => Stmt::FnCall(x, pos), + // {}; + Expr::Stmt(x) if x.is_empty() => Stmt::Noop(x.position()), + // {...}; + Expr::Stmt(x) => (*x).into(), + _ => unreachable!(), + }; } } diff --git a/src/parser.rs b/src/parser.rs index 23f96d7b..93094aed 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -193,7 +193,6 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_function"))] let is_func_name = _lib.values().any(|f| f.name == name); - #[cfg(feature = "no_function")] let is_func_name = false; @@ -600,7 +599,9 @@ impl Engine { #[cfg(feature = "no_module")] let hash = calc_fn_hash(None, &id, 0); - let hashes = if is_valid_function_name(&id) { + let is_valid_function_name = is_valid_function_name(&id); + + let hashes = if is_valid_function_name { hash.into() } else { FnCallHashes::from_native(hash) @@ -612,6 +613,8 @@ impl Engine { name: state.get_interned_string(id), capture_parent_scope, operator_token: None, + #[cfg(not(feature = "no_function"))] + can_be_script: is_valid_function_name, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -668,7 +671,9 @@ impl Engine { #[cfg(feature = "no_module")] let hash = calc_fn_hash(None, &id, args.len()); - let hashes = if is_valid_function_name(&id) { + let is_valid_function_name = is_valid_function_name(&id); + + let hashes = if is_valid_function_name { hash.into() } else { FnCallHashes::from_native(hash) @@ -680,6 +685,8 @@ impl Engine { name: state.get_interned_string(id), capture_parent_scope, operator_token: None, + #[cfg(not(feature = "no_function"))] + can_be_script: is_valid_function_name, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -1912,12 +1919,14 @@ impl Engine { args.shrink_to_fit(); Ok(FnCallExpr { + namespace: Default::default(), name: state.get_interned_string("-"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), args, pos, operator_token: Some(token), - ..Default::default() + capture_parent_scope: false, + can_be_script: false, } .into_fn_call_expr(pos)) } @@ -1940,12 +1949,14 @@ impl Engine { args.shrink_to_fit(); Ok(FnCallExpr { + namespace: Default::default(), name: state.get_interned_string("+"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), args, pos, operator_token: Some(token), - ..Default::default() + capture_parent_scope: false, + can_be_script: false, } .into_fn_call_expr(pos)) } @@ -1961,12 +1972,14 @@ impl Engine { args.shrink_to_fit(); Ok(FnCallExpr { + namespace: Default::default(), name: state.get_interned_string("!"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), args, pos, operator_token: Some(token), - ..Default::default() + capture_parent_scope: false, + can_be_script: false, } .into_fn_call_expr(pos)) } @@ -2335,18 +2348,22 @@ impl Engine { let op = op_token.syntax(); let hash = calc_fn_hash(None, &op, 2); - let operator_token = if is_valid_function_name(&op) { + let is_function = is_valid_function_name(&op); + let operator_token = if is_function { None } else { Some(op_token.clone()) }; let op_base = FnCallExpr { + namespace: Default::default(), name: state.get_interned_string(op.as_ref()), hashes: FnCallHashes::from_native(hash), + args: StaticVec::new_const(), pos, operator_token, - ..Default::default() + capture_parent_scope: false, + can_be_script: is_function, }; let mut args = StaticVec::new_const(); @@ -2432,7 +2449,7 @@ impl Engine { let pos = args[0].start_position(); FnCallExpr { - hashes: if is_valid_function_name(&s) { + hashes: if is_function { hash.into() } else { FnCallHashes::from_native(hash) @@ -3659,6 +3676,7 @@ impl Engine { ); let expr = FnCallExpr { + namespace: Default::default(), name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY), hashes: FnCallHashes::from_native(calc_fn_hash( None, @@ -3667,7 +3685,9 @@ impl Engine { )), args, pos, - ..Default::default() + operator_token: None, + capture_parent_scope: false, + can_be_script: false, } .into_fn_call_expr(pos); From 5035dcbf4734ecf4c2cbaa59b8f4c49c707fccce Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 14 Oct 2022 18:43:25 +0800 Subject: [PATCH 040/116] Fix builds. --- src/parser.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 93094aed..6c100008 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1919,6 +1919,7 @@ impl Engine { args.shrink_to_fit(); Ok(FnCallExpr { + #[cfg(not(feature = "no_module"))] namespace: Default::default(), name: state.get_interned_string("-"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), @@ -1926,6 +1927,7 @@ impl Engine { pos, operator_token: Some(token), capture_parent_scope: false, + #[cfg(not(feature = "no_function"))] can_be_script: false, } .into_fn_call_expr(pos)) @@ -1949,6 +1951,7 @@ impl Engine { args.shrink_to_fit(); Ok(FnCallExpr { + #[cfg(not(feature = "no_module"))] namespace: Default::default(), name: state.get_interned_string("+"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), @@ -1956,6 +1959,7 @@ impl Engine { pos, operator_token: Some(token), capture_parent_scope: false, + #[cfg(not(feature = "no_function"))] can_be_script: false, } .into_fn_call_expr(pos)) @@ -1972,6 +1976,7 @@ impl Engine { args.shrink_to_fit(); Ok(FnCallExpr { + #[cfg(not(feature = "no_module"))] namespace: Default::default(), name: state.get_interned_string("!"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), @@ -1979,6 +1984,7 @@ impl Engine { pos, operator_token: Some(token), capture_parent_scope: false, + #[cfg(not(feature = "no_function"))] can_be_script: false, } .into_fn_call_expr(pos)) @@ -2356,6 +2362,7 @@ impl Engine { }; let op_base = FnCallExpr { + #[cfg(not(feature = "no_module"))] namespace: Default::default(), name: state.get_interned_string(op.as_ref()), hashes: FnCallHashes::from_native(hash), @@ -2363,6 +2370,7 @@ impl Engine { pos, operator_token, capture_parent_scope: false, + #[cfg(not(feature = "no_function"))] can_be_script: is_function, }; @@ -3676,6 +3684,7 @@ impl Engine { ); let expr = FnCallExpr { + #[cfg(not(feature = "no_module"))] namespace: Default::default(), name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY), hashes: FnCallHashes::from_native(calc_fn_hash( @@ -3687,6 +3696,7 @@ impl Engine { pos, operator_token: None, capture_parent_scope: false, + #[cfg(not(feature = "no_function"))] can_be_script: false, } .into_fn_call_expr(pos); From 9b226f321ec36cf611da7a57519c6b90201a84b8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 15 Oct 2022 12:04:14 +0800 Subject: [PATCH 041/116] Use template to create config.rs. --- build.rs | 30 +++++++++++++++--------------- build.template | 5 +++++ src/config.rs | 2 ++ 3 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 build.template diff --git a/build.rs b/build.rs index a8fa7496..fa7570c5 100644 --- a/build.rs +++ b/build.rs @@ -1,26 +1,26 @@ -use std::{env, fs::File, io::Write}; - -const WRITE_ERROR: &str = "cannot write to `config.rs`"; +use std::{ + env, + fs::File, + io::{Read, Write}, +}; fn main() { // Tell Cargo that if the given environment variable changes, to rerun this build script. println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); - let mut f = File::create("src/config.rs").expect("cannot create `config.rs`"); + let mut contents = String::new(); - f.write_fmt(format_args!( - "//! Configuration settings for this Rhai build - -" - )) - .expect(WRITE_ERROR); + File::open("build.template") + .expect("cannot open `build.template`") + .read_to_string(&mut contents) + .expect("cannot read from `build.template`"); let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); - f.write_fmt(format_args!( - "pub const AHASH_SEED: Option<[u64; 4]> = {seed};\n" - )) - .expect(WRITE_ERROR); + contents = contents.replace("{{AHASH_SEED}}", &seed); - f.flush().expect("cannot flush `config.rs`"); + File::create("src/config.rs") + .expect("cannot create `config.rs`") + .write(contents.as_bytes()) + .expect("cannot write to `config.rs`"); } diff --git a/build.template b/build.template new file mode 100644 index 00000000..b55d13e8 --- /dev/null +++ b/build.template @@ -0,0 +1,5 @@ +//! Configuration settings for this Rhai build + +/// Fixed hashing seeds for stable hashing. +/// Set to [`None`] to disable stable hashing. +pub const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; diff --git a/src/config.rs b/src/config.rs index 91e4716a..9eb456fd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ //! Configuration settings for this Rhai build +/// Fixed hashing seeds for stable hashing. +/// Set to [`None`] to disable stable hashing. pub const AHASH_SEED: Option<[u64; 4]> = None; From a6a570131acb554ec1c99bab9bd5e84d9346d2e7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 15 Oct 2022 12:37:42 +0800 Subject: [PATCH 042/116] Add no_time. --- CHANGELOG.md | 5 +++++ Cargo.toml | 1 + src/api/definitions/mod.rs | 1 + src/api/type_names.rs | 1 + src/lib.rs | 1 + src/packages/mod.rs | 1 + src/packages/pkg_std.rs | 2 +- src/packages/time_basic.rs | 1 + src/serde/de.rs | 1 + src/serde/serialize.rs | 2 ++ src/types/dynamic.rs | 22 +++++++++++++++++++++- src/types/mod.rs | 1 + 12 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffcc3516..61d09c7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ New features * This is necessary when using Rhai across shared-library boundaries. * A build script is used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. +### No Timestamps + +* A new feature, `no_time`, is added to disable support timestamps. +* This may be necessary when building for architectures without time support, such as raw WASM. + ### Serializable `Scope` * `Scope` is now serializable and deserializable via `serde`. diff --git a/Cargo.toml b/Cargo.toml index 5b506221..ff8a8b69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ no_function = ["no_closure"] # no script-defined functions (meaning no closur no_closure = [] # no automatic sharing and capture of anonymous functions to external variables no_module = [] # no modules no_custom_syntax = [] # no custom syntax or custom operators +no_time = [] # no timestamps unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index cd0a51c2..4ac1280d 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -556,6 +556,7 @@ fn def_type_name<'a>(ty: &'a str, engine: &'a Engine) -> Cow<'a, str> { let ty = ty.replace(type_name::(), "Map"); #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] let ty = ty.replace(type_name::(), "Instant"); let ty = ty.replace(type_name::(), "FnPtr"); diff --git a/src/api/type_names.rs b/src/api/type_names.rs index f426154b..eb0f9857 100644 --- a/src/api/type_names.rs +++ b/src/api/type_names.rs @@ -45,6 +45,7 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str { return if shorthands { "map" } else { "Map" }; } #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] if name == type_name::() || name == "Instant" { return if shorthands { "timestamp" } else { "Instant" }; } diff --git a/src/lib.rs b/src/lib.rs index 7b36c9cf..9a540e3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,6 +205,7 @@ pub use func::{NativeCallContext, RegisterNativeFunction}; pub use module::{FnNamespace, Module}; pub use tokenizer::Position; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] pub use types::Instant; pub use types::{ Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope, diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 5a63e86b..c9f31bcf 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -39,6 +39,7 @@ pub use pkg_std::StandardPackage; pub use string_basic::BasicStringPackage; pub use string_more::MoreStringPackage; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] pub use time_basic::BasicTimePackage; /// Trait that all packages must implement. diff --git a/src/packages/pkg_std.rs b/src/packages/pkg_std.rs index e7d4f1ff..cdc5fbbf 100644 --- a/src/packages/pkg_std.rs +++ b/src/packages/pkg_std.rs @@ -26,7 +26,7 @@ def_package! { #[cfg(not(feature = "no_index"))] BasicArrayPackage, #[cfg(not(feature = "no_index"))] BasicBlobPackage, #[cfg(not(feature = "no_object"))] BasicMapPackage, - #[cfg(not(feature = "no_std"))] BasicTimePackage, + #[cfg(all(not(feature = "no_std"), not(feature = "no_time")))] BasicTimePackage, MoreStringPackage { lib.standard = true; diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 16ade536..e76cc06e 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,4 +1,5 @@ #![cfg(not(feature = "no_std"))] +#![cfg(not(feature = "no_time"))] use super::arithmetic::make_err as make_arithmetic_err; use crate::plugin::*; diff --git a/src/serde/de.rs b/src/serde/de.rs index 8f4cd237..4dd0b133 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -157,6 +157,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { Union::Map(..) => self.deserialize_map(visitor), Union::FnPtr(..) => self.type_error(), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => self.type_error(), Union::Variant(ref value, ..) if value.is::() => self.deserialize_i8(visitor), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 69a280ef..509dad56 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -10,6 +10,7 @@ use std::prelude::v1::*; use serde::ser::SerializeMap; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] use crate::types::dynamic::Variant; impl Serialize for Dynamic { @@ -66,6 +67,7 @@ impl Serialize for Dynamic { } Union::FnPtr(ref f, ..) => ser.serialize_str(f.fn_name()), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()), Union::Variant(ref v, ..) => ser.serialize_str((***v).type_name()), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 6572dbe4..5643901a 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -15,10 +15,12 @@ use std::{ pub use super::Variant; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] #[cfg(not(target_family = "wasm"))] pub use std::time::Instant; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] #[cfg(target_family = "wasm")] pub use instant::Instant; @@ -85,6 +87,7 @@ pub enum Union { FnPtr(Box, Tag, AccessMode), /// A timestamp value. #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] TimeStamp(Box, Tag, AccessMode), /// Any type as a trait object. @@ -195,6 +198,7 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(_, tag, _) => tag, #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(_, tag, _) => tag, #[cfg(not(feature = "no_closure"))] Union::Shared(_, tag, _) => tag, @@ -220,6 +224,7 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_closure"))] Union::Shared(_, ref mut tag, _) => *tag = value, @@ -292,6 +297,7 @@ impl Dynamic { return matches!(self.0, Union::FnPtr(..)); } #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::TimeStamp(..)); } @@ -324,6 +330,7 @@ impl Dynamic { Union::Map(..) => TypeId::of::(), Union::FnPtr(..) => TypeId::of::(), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => TypeId::of::(), Union::Variant(ref v, ..) => (***v).type_id(), @@ -358,6 +365,7 @@ impl Dynamic { Union::Map(..) => "map", Union::FnPtr(..) => "Fn", #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => "timestamp", Union::Variant(ref v, ..) => (***v).type_name(), @@ -408,6 +416,7 @@ impl Hash for Dynamic { Union::Variant(..) => unimplemented!("{} cannot be hashed", self.type_name()), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => unimplemented!("{} cannot be hashed", self.type_name()), } } @@ -433,6 +442,7 @@ impl fmt::Display for Dynamic { Union::Map(..) => fmt::Debug::fmt(self, f), Union::FnPtr(ref v, ..) => fmt::Display::fmt(v, f), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => f.write_str(""), Union::Variant(ref v, ..) => { @@ -538,6 +548,7 @@ impl fmt::Debug for Dynamic { } Union::FnPtr(ref v, ..) => fmt::Debug::fmt(v, f), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => write!(f, ""), Union::Variant(ref v, ..) => { @@ -636,6 +647,7 @@ impl Clone for Dynamic { Union::Map(ref v, tag, ..) => Self(Union::Map(v.clone(), tag, ReadWrite)), Union::FnPtr(ref v, tag, ..) => Self(Union::FnPtr(v.clone(), tag, ReadWrite)), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref v, tag, ..) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)), Union::Variant(ref v, tag, ..) => Self(Union::Variant( @@ -876,8 +888,9 @@ impl Dynamic { } /// Create a new [`Dynamic`] from an [`Instant`]. /// - /// Not available under `no-std`. + /// Not available under `no-std` or `no_time`. #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] #[inline(always)] #[must_use] pub fn from_timestamp(value: Instant) -> Self { @@ -905,6 +918,7 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(.., access) => access, #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(.., access) => access, #[cfg(not(feature = "no_closure"))] Union::Shared(.., access) => access, @@ -942,6 +956,7 @@ impl Dynamic { } } #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(.., ref mut access) => *access = typ, #[cfg(not(feature = "no_closure"))] Union::Shared(.., ref mut access) => *access = typ, @@ -1076,6 +1091,7 @@ impl Dynamic { reify!(value, |v: FnPtr| return v.into()); #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] reify!(value, |v: Instant| return v.into()); #[cfg(not(feature = "no_closure"))] reify!(value, |v: crate::Shared>| return v @@ -1169,6 +1185,7 @@ impl Dynamic { Union::Map(v, ..) => reify!(*v => Option), Union::FnPtr(v, ..) => reify!(*v => Option), #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] Union::TimeStamp(v, ..) => reify!(*v => Option), Union::Unit(v, ..) => reify!(v => Option), Union::Variant(v, ..) => (*v).as_boxed_any().downcast().ok().map(|x| *x), @@ -1468,6 +1485,7 @@ impl Dynamic { }; } #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { Union::TimeStamp(ref v, ..) => v.as_ref().as_any().downcast_ref::(), @@ -1566,6 +1584,7 @@ impl Dynamic { }; } #[cfg(not(feature = "no_std"))] + #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { Union::TimeStamp(ref mut v, ..) => v.as_mut().as_any_mut().downcast_mut::(), @@ -1960,6 +1979,7 @@ impl From for Dynamic { } } #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] impl From for Dynamic { #[inline(always)] fn from(value: Instant) -> Self { diff --git a/src/types/mod.rs b/src/types/mod.rs index 475ca2c9..8de89ca8 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -15,6 +15,7 @@ pub use bloom_filter::BloomFilterU64; pub use custom_types::{CustomTypeInfo, CustomTypesCollection}; pub use dynamic::Dynamic; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_time"))] pub use dynamic::Instant; pub use error::EvalAltResult; pub use fn_ptr::FnPtr; From 45f0fdcbe07057d74d4ed9dd3f62e0592e17e7ce Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 15 Oct 2022 15:11:51 +0800 Subject: [PATCH 043/116] no_std = no_time. --- .github/workflows/build.yml | 5 +++-- Cargo.toml | 4 ++-- src/api/definitions/mod.rs | 1 - src/api/type_names.rs | 1 - src/lib.rs | 1 - src/packages/mod.rs | 1 - src/packages/pkg_std.rs | 2 +- src/packages/time_basic.rs | 1 - src/serde/de.rs | 1 - src/serde/serialize.rs | 2 -- src/types/dynamic.rs | 20 -------------------- src/types/mod.rs | 1 - 12 files changed, 6 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a72d1d4..6823d676 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,10 +60,11 @@ jobs: - "--features no_object,serde,metadata,internals,debugging" - "--features no_function,serde,metadata,internals,debugging" - "--features no_module,serde,metadata,internals,debugging" + - "--features no_time,serde,metadata,internals,debugging" - "--features no_closure,serde,metadata,internals,debugging" - "--features unicode-xid-ident,serde,metadata,internals,debugging" - - "--features sync,no_function,no_float,no_position,no_optimize,no_module,no_closure,no_custom_syntax,metadata,serde,unchecked,debugging" - - "--features no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,no_custom_syntax,unchecked" + - "--features sync,no_time,no_function,no_float,no_position,no_optimize,no_module,no_closure,no_custom_syntax,metadata,serde,unchecked,debugging" + - "--features no_time,no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,no_custom_syntax,unchecked" toolchain: [stable] experimental: [false] include: diff --git a/Cargo.toml b/Cargo.toml index ff8a8b69..e1f3bd4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,11 +53,11 @@ only_i64 = [] # set INT=i64 (default) and disable support for decimal = ["rust_decimal"] # add the Decimal number type no_index = [] # no arrays and indexing no_object = [] # no custom objects +no_time = [] # no timestamps no_function = ["no_closure"] # no script-defined functions (meaning no closures) no_closure = [] # no automatic sharing and capture of anonymous functions to external variables no_module = [] # no modules no_custom_syntax = [] # no custom syntax or custom operators -no_time = [] # no timestamps unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures @@ -65,7 +65,7 @@ debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std -no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown"] +no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown", "no_time"] # compiling for WASM wasm-bindgen = ["instant/wasm-bindgen"] diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 4ac1280d..7be44611 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -555,7 +555,6 @@ fn def_type_name<'a>(ty: &'a str, engine: &'a Engine) -> Cow<'a, str> { #[cfg(not(feature = "no_object"))] let ty = ty.replace(type_name::(), "Map"); - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] let ty = ty.replace(type_name::(), "Instant"); diff --git a/src/api/type_names.rs b/src/api/type_names.rs index eb0f9857..f7897bf0 100644 --- a/src/api/type_names.rs +++ b/src/api/type_names.rs @@ -44,7 +44,6 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str { if name == type_name::() || name == "Map" { return if shorthands { "map" } else { "Map" }; } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if name == type_name::() || name == "Instant" { return if shorthands { "timestamp" } else { "Instant" }; diff --git a/src/lib.rs b/src/lib.rs index 9a540e3a..199edb75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,7 +204,6 @@ pub use eval::EvalContext; pub use func::{NativeCallContext, RegisterNativeFunction}; pub use module::{FnNamespace, Module}; pub use tokenizer::Position; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] pub use types::Instant; pub use types::{ diff --git a/src/packages/mod.rs b/src/packages/mod.rs index c9f31bcf..499a1762 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -38,7 +38,6 @@ pub use pkg_core::CorePackage; pub use pkg_std::StandardPackage; pub use string_basic::BasicStringPackage; pub use string_more::MoreStringPackage; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] pub use time_basic::BasicTimePackage; diff --git a/src/packages/pkg_std.rs b/src/packages/pkg_std.rs index cdc5fbbf..b054b3ae 100644 --- a/src/packages/pkg_std.rs +++ b/src/packages/pkg_std.rs @@ -26,7 +26,7 @@ def_package! { #[cfg(not(feature = "no_index"))] BasicArrayPackage, #[cfg(not(feature = "no_index"))] BasicBlobPackage, #[cfg(not(feature = "no_object"))] BasicMapPackage, - #[cfg(all(not(feature = "no_std"), not(feature = "no_time")))] BasicTimePackage, + #[cfg(not(feature = "no_time"))] BasicTimePackage, MoreStringPackage { lib.standard = true; diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index e76cc06e..cf7f3dc4 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,4 +1,3 @@ -#![cfg(not(feature = "no_std"))] #![cfg(not(feature = "no_time"))] use super::arithmetic::make_err as make_arithmetic_err; diff --git a/src/serde/de.rs b/src/serde/de.rs index 4dd0b133..fcb26a9d 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -156,7 +156,6 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { #[cfg(not(feature = "no_object"))] Union::Map(..) => self.deserialize_map(visitor), Union::FnPtr(..) => self.type_error(), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => self.type_error(), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 509dad56..424de0d5 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -9,7 +9,6 @@ use std::prelude::v1::*; #[cfg(not(feature = "no_object"))] use serde::ser::SerializeMap; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] use crate::types::dynamic::Variant; @@ -66,7 +65,6 @@ impl Serialize for Dynamic { map.end() } Union::FnPtr(ref f, ..) => ser.serialize_str(f.fn_name()), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 5643901a..0c9b7661 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -14,12 +14,10 @@ use std::{ pub use super::Variant; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] #[cfg(not(target_family = "wasm"))] pub use std::time::Instant; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] #[cfg(target_family = "wasm")] pub use instant::Instant; @@ -86,7 +84,6 @@ pub enum Union { /// A function pointer. FnPtr(Box, Tag, AccessMode), /// A timestamp value. - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] TimeStamp(Box, Tag, AccessMode), @@ -197,7 +194,6 @@ impl Dynamic { Union::Array(_, tag, _) | Union::Blob(_, tag, _) => tag, #[cfg(not(feature = "no_object"))] Union::Map(_, tag, _) => tag, - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(_, tag, _) => tag, #[cfg(not(feature = "no_closure"))] @@ -223,7 +219,6 @@ impl Dynamic { Union::Array(_, ref mut tag, _) | Union::Blob(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_object"))] Union::Map(_, ref mut tag, _) => *tag = value, - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_closure"))] @@ -296,7 +291,6 @@ impl Dynamic { if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::FnPtr(..)); } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::TimeStamp(..)); @@ -329,7 +323,6 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(..) => TypeId::of::(), Union::FnPtr(..) => TypeId::of::(), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => TypeId::of::(), @@ -364,7 +357,6 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(..) => "map", Union::FnPtr(..) => "Fn", - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => "timestamp", @@ -415,7 +407,6 @@ impl Hash for Dynamic { Union::Variant(..) => unimplemented!("{} cannot be hashed", self.type_name()), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => unimplemented!("{} cannot be hashed", self.type_name()), } @@ -441,7 +432,6 @@ impl fmt::Display for Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(..) => fmt::Debug::fmt(self, f), Union::FnPtr(ref v, ..) => fmt::Display::fmt(v, f), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => f.write_str(""), @@ -547,7 +537,6 @@ impl fmt::Debug for Dynamic { fmt::Debug::fmt(v, f) } Union::FnPtr(ref v, ..) => fmt::Debug::fmt(v, f), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => write!(f, ""), @@ -646,7 +635,6 @@ impl Clone for Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(ref v, tag, ..) => Self(Union::Map(v.clone(), tag, ReadWrite)), Union::FnPtr(ref v, tag, ..) => Self(Union::FnPtr(v.clone(), tag, ReadWrite)), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref v, tag, ..) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)), @@ -889,7 +877,6 @@ impl Dynamic { /// Create a new [`Dynamic`] from an [`Instant`]. /// /// Not available under `no-std` or `no_time`. - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] #[inline(always)] #[must_use] @@ -917,7 +904,6 @@ impl Dynamic { Union::Array(.., access) | Union::Blob(.., access) => access, #[cfg(not(feature = "no_object"))] Union::Map(.., access) => access, - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(.., access) => access, #[cfg(not(feature = "no_closure"))] @@ -955,7 +941,6 @@ impl Dynamic { v.set_access_mode(typ); } } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(.., ref mut access) => *access = typ, #[cfg(not(feature = "no_closure"))] @@ -1090,7 +1075,6 @@ impl Dynamic { reify!(value, |v: crate::Map| return v.into()); reify!(value, |v: FnPtr| return v.into()); - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] reify!(value, |v: Instant| return v.into()); #[cfg(not(feature = "no_closure"))] @@ -1184,7 +1168,6 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(v, ..) => reify!(*v => Option), Union::FnPtr(v, ..) => reify!(*v => Option), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(v, ..) => reify!(*v => Option), Union::Unit(v, ..) => reify!(v => Option), @@ -1484,7 +1467,6 @@ impl Dynamic { _ => None, }; } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { @@ -1583,7 +1565,6 @@ impl Dynamic { _ => None, }; } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { @@ -1978,7 +1959,6 @@ impl From for Dynamic { Self(Union::FnPtr(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] impl From for Dynamic { #[inline(always)] diff --git a/src/types/mod.rs b/src/types/mod.rs index 8de89ca8..b6ca3802 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -14,7 +14,6 @@ pub mod variant; pub use bloom_filter::BloomFilterU64; pub use custom_types::{CustomTypeInfo, CustomTypesCollection}; pub use dynamic::Dynamic; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] pub use dynamic::Instant; pub use error::EvalAltResult; From 530109275ff9b544ff88934580e3c1cfa03e63b7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 16 Oct 2022 11:35:21 +0800 Subject: [PATCH 044/116] Minor code restructure. --- src/api/mod.rs | 77 ++++++++++++++++++++++++++++++++--- src/ast/expr.rs | 12 +++--- src/engine.rs | 92 +----------------------------------------- src/eval/data_check.rs | 12 +++++- src/eval/mod.rs | 33 +++++++++++++++ src/optimizer.rs | 23 ++++++----- 6 files changed, 137 insertions(+), 112 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index c296c81e..5f85f140 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -35,9 +35,6 @@ pub mod definitions; use crate::{Dynamic, Engine, Identifier}; -#[cfg(not(feature = "no_custom_syntax"))] -use crate::{engine::Precedence, tokenizer::Token}; - #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -150,8 +147,10 @@ impl Engine { keyword: impl AsRef, precedence: u8, ) -> Result<&mut Self, String> { - let precedence = - Precedence::new(precedence).ok_or_else(|| "precedence cannot be zero".to_string())?; + use crate::tokenizer::Token; + + let precedence = crate::engine::Precedence::new(precedence) + .ok_or_else(|| "precedence cannot be zero".to_string())?; let keyword = keyword.as_ref(); @@ -215,3 +214,71 @@ impl Engine { self } } + +#[cfg(feature = "unchecked")] +impl Engine { + /// The maximum levels of function calls allowed for a script. + /// + /// Always returns [`usize::MAX`] under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_call_levels(&self) -> usize { + usize::MAX + } + /// The maximum number of operations allowed for a script to run (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_operations(&self) -> u64 { + 0 + } + /// The maximum number of imported [modules][crate::Module] allowed for a script. + /// + /// Always returns [`usize::MAX`] under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_modules(&self) -> usize { + usize::MAX + } + /// The depth limit for expressions (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_expr_depth(&self) -> usize { + 0 + } + /// The depth limit for expressions in functions (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_function_expr_depth(&self) -> usize { + 0 + } + /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_string_size(&self) -> usize { + 0 + } + /// The maximum length of [arrays][crate::Array] (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_array_size(&self) -> usize { + 0 + } + /// The maximum size of [object maps][crate::Map] (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_map_size(&self) -> usize { + 0 + } +} diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 4adaaecf..72d866fc 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -212,19 +212,19 @@ impl fmt::Debug for FnCallExpr { if !self.namespace.is_empty() { ff.field("namespace", &self.namespace); } - if self.capture_parent_scope { - ff.field("capture_parent_scope", &self.capture_parent_scope); - } + ff.field("hash", &self.hashes) + .field("name", &self.name) + .field("args", &self.args); if let Some(ref token) = self.operator_token { ff.field("operator_token", token); } + if self.capture_parent_scope { + ff.field("capture_parent_scope", &self.capture_parent_scope); + } #[cfg(not(feature = "no_function"))] if self.can_be_script { ff.field("can_be_script", &self.can_be_script); } - ff.field("hash", &self.hashes) - .field("name", &self.name) - .field("args", &self.args); ff.field("pos", &self.pos); ff.finish() } diff --git a/src/engine.rs b/src/engine.rs index 323f05b5..8edc62ff 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,8 +9,7 @@ use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; use crate::types::StringsInterner; use crate::{ - Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Position, RhaiResult, - Shared, StaticVec, + Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Shared, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -345,93 +344,4 @@ impl Engine { pub fn const_empty_string(&self) -> ImmutableString { self.get_interned_string("") } - - /// Check a result to ensure that it is valid. - #[cfg(not(feature = "unchecked"))] - #[inline] - pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult { - if let Ok(ref r) = result { - self.check_data_size(r, _pos)?; - } - - result - } -} - -#[cfg(feature = "unchecked")] -impl Engine { - /// The maximum levels of function calls allowed for a script. - #[inline(always)] - #[must_use] - pub const fn max_call_levels(&self) -> usize { - usize::MAX - } - /// The maximum number of operations allowed for a script to run (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_operations(&self) -> u64 { - 0 - } - /// The maximum number of imported [modules][crate::Module] allowed for a script. - #[inline(always)] - #[must_use] - pub const fn max_modules(&self) -> usize { - usize::MAX - } - /// The depth limit for expressions (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_expr_depth(&self) -> usize { - 0 - } - /// The depth limit for expressions in functions (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_function_expr_depth(&self) -> usize { - 0 - } - /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_string_size(&self) -> usize { - 0 - } - /// The maximum length of [arrays][crate::Array] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_array_size(&self) -> usize { - 0 - } - /// The maximum size of [object maps][crate::Map] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_map_size(&self) -> usize { - 0 - } - - /// Check if the number of operations stay within limit. - #[inline(always)] - pub(crate) const fn track_operation( - &self, - _: &crate::eval::GlobalRuntimeState, - _: Position, - ) -> crate::RhaiResultOf<()> { - Ok(()) - } - - /// Check whether the size of a [`Dynamic`] is within limits. - #[inline(always)] - pub(crate) const fn check_data_size( - &self, - _: &Dynamic, - _: Position, - ) -> crate::RhaiResultOf<()> { - Ok(()) - } - - /// Check a result to ensure that it is valid. - #[inline(always)] - pub(crate) const fn check_return_value(&self, result: RhaiResult, _: Position) -> RhaiResult { - result - } } diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 76cf7a5c..bc8953ca 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -3,7 +3,7 @@ use super::GlobalRuntimeState; use crate::types::dynamic::Union; -use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR}; +use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -150,4 +150,14 @@ impl Engine { Ok(()) } + + /// Check a result to ensure that it is valid. + #[inline] + pub(crate) fn check_return_value(&self, result: RhaiResult, pos: Position) -> RhaiResult { + if let Ok(ref r) = result { + self.check_data_size(r, pos)?; + } + + result + } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 2daf2432..1daf85ae 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -22,3 +22,36 @@ pub use eval_context::EvalContext; pub use global_state::GlobalConstants; pub use global_state::GlobalRuntimeState; pub use target::{calc_index, calc_offset_len, Target}; + +#[cfg(feature = "unchecked")] +mod unchecked { + use crate::{eval::GlobalRuntimeState, Dynamic, Engine, Position, RhaiResult, RhaiResultOf}; + + impl Engine { + /// Check if the number of operations stay within limit. + #[inline(always)] + pub(crate) const fn track_operation( + &self, + _: &GlobalRuntimeState, + _: Position, + ) -> RhaiResultOf<()> { + Ok(()) + } + + /// Check whether the size of a [`Dynamic`] is within limits. + #[inline(always)] + pub(crate) const fn check_data_size(&self, _: &Dynamic, _: Position) -> RhaiResultOf<()> { + Ok(()) + } + + /// Check a result to ensure that it is valid. + #[inline(always)] + pub(crate) const fn check_return_value( + &self, + result: RhaiResult, + _: Position, + ) -> RhaiResult { + result + } + } +} diff --git a/src/optimizer.rs b/src/optimizer.rs index dea68ea3..20145659 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -175,21 +175,26 @@ fn has_native_fn_override( // First check the global namespace and packages, but skip modules that are standard because // they should never conflict with system functions. - let result = engine + if engine .global_modules .iter() .filter(|m| !m.standard) - .any(|m| m.contains_fn(hash)); + .any(|m| m.contains_fn(hash)) + { + return true; + } - #[cfg(not(feature = "no_module"))] // Then check sub-modules - let result = result - || engine - .global_sub_modules - .values() - .any(|m| m.contains_qualified_fn(hash)); + #[cfg(not(feature = "no_module"))] + if engine + .global_sub_modules + .values() + .any(|m| m.contains_qualified_fn(hash)) + { + return true; + } - result + false } /// Optimize a block of [statements][Stmt]. From 3e46263347ba18732eabad04c9fbe521bacca46c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 16 Oct 2022 12:07:47 +0800 Subject: [PATCH 045/116] Fix tests. --- tests/switch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/switch.rs b/tests/switch.rs index 8008ccf2..2a4aed98 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -52,7 +52,7 @@ fn test_switch() -> Result<(), Box> { 123 ); assert_eq!( - engine.eval::("let x = timestamp(); switch x { 1 => 123, _ => 42 }")?, + engine.eval_with_scope::(&mut scope, "switch x { 424242 => 123, _ => 42 }")?, 42 ); assert_eq!( From c5a111ddb2498f4552e625a0bc833eaf0a887004 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 17 Oct 2022 11:54:25 +0800 Subject: [PATCH 046/116] Set getrandom/js with wasm-bindgen or stdweb --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e1f3bd4e..d52f8b30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ serde = { version = "1.0", default-features = false, features = ["derive", "allo serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } unicode-xid = { version = "0.2", default-features = false, optional = true } rust_decimal = { version = "1.16", default-features = false, features = ["maths"], optional = true } +getrandom = { version = "0.2", optional = true } rustyline = { version = "10", optional = true } [dev-dependencies] @@ -68,8 +69,8 @@ serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde f no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown", "no_time"] # compiling for WASM -wasm-bindgen = ["instant/wasm-bindgen"] -stdweb = ["instant/stdweb"] +wasm-bindgen = ["getrandom/js", "instant/wasm-bindgen"] +stdweb = ["getrandom/js", "instant/stdweb"] # compiling bin tools bin-features = ["decimal", "metadata", "serde", "debugging", "rustyline"] From 4642895de8b44e81e8ae085af6e399fb3b6bae19 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 18 Oct 2022 15:44:24 +0800 Subject: [PATCH 047/116] Change loops to iterators. --- src/api/definitions/mod.rs | 20 +++-- src/api/json.rs | 5 +- src/eval/expr.rs | 161 ++++++++++++++++--------------------- src/eval/stmt.rs | 134 +++++++++++++----------------- 4 files changed, 137 insertions(+), 183 deletions(-) diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 7be44611..18d9c992 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -440,18 +440,16 @@ impl Module { } first = false; - if f.access == FnAccess::Private { - continue; + if f.access != FnAccess::Private { + #[cfg(not(feature = "no_custom_syntax"))] + let operator = def.engine.custom_keywords.contains_key(&f.name) + || (!f.name.contains('$') && !is_valid_function_name(&f.name)); + + #[cfg(feature = "no_custom_syntax")] + let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); + + f.write_definition(writer, def, operator)?; } - - #[cfg(not(feature = "no_custom_syntax"))] - let operator = def.engine.custom_keywords.contains_key(&f.name) - || (!f.name.contains('$') && !is_valid_function_name(&f.name)); - - #[cfg(feature = "no_custom_syntax")] - let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); - - f.write_definition(writer, def, operator)?; } Ok(()) diff --git a/src/api/json.rs b/src/api/json.rs index 24069fdd..c22d682c 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -166,10 +166,7 @@ pub fn format_map_as_json(map: &Map) -> String { if let Some(val) = value.read_lock::() { result.push_str(&format_map_as_json(&*val)); - continue; - } - - if value.is::<()>() { + } else if value.is::<()>() { result.push_str("null"); } else { write!(result, "{:?}", value).unwrap(); diff --git a/src/eval/expr.rs b/src/eval/expr.rs index c09b48a3..d86c3da6 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -379,114 +379,93 @@ impl Engine { Expr::InterpolatedString(x, _) => { let mut concat = self.get_interned_string("").into(); let target = &mut concat; - let mut result = Ok(Dynamic::UNIT); let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE); let root = ("", Position::NONE); - for expr in &**x { - let item = - match self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) { - Ok(r) => r, - err => { - result = err; - break; - } - }; + let result = x + .iter() + .try_for_each(|expr| { + let item = + self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?; - op_info.pos = expr.start_position(); + op_info.pos = expr.start_position(); - if let Err(err) = self.eval_op_assignment( - global, caches, lib, &op_info, target, root, item, level, - ) { - result = Err(err); - break; - } - } + self.eval_op_assignment( + global, caches, lib, &op_info, target, root, item, level, + ) + }) + .map(|_| concat.take_or_clone()); - self.check_return_value( - result.map(|_| concat.take_or_clone()), - expr.start_position(), - ) + self.check_return_value(result, expr.start_position()) } #[cfg(not(feature = "no_index"))] Expr::Array(x, ..) => { - let mut array = crate::Array::with_capacity(x.len()); - let mut result = Ok(Dynamic::UNIT); - #[cfg(not(feature = "unchecked"))] let mut total_data_sizes = (0, 0, 0); - for item_expr in &**x { - let value = match self - .eval_expr(scope, global, caches, lib, this_ptr, item_expr, level) - { - Ok(r) => r.flatten(), - err => { - result = err; - break; - } - }; + x.iter() + .try_fold( + crate::Array::with_capacity(x.len()), + |mut array, item_expr| { + let value = self + .eval_expr(scope, global, caches, lib, this_ptr, item_expr, level)? + .flatten(); - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - let val_sizes = Self::calc_data_sizes(&value, true); + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + let val_sizes = Self::calc_data_sizes(&value, true); - total_data_sizes = ( - total_data_sizes.0 + val_sizes.0, - total_data_sizes.1 + val_sizes.1, - total_data_sizes.2 + val_sizes.2, - ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - item_expr.position(), - )?; - } + total_data_sizes = ( + total_data_sizes.0 + val_sizes.0, + total_data_sizes.1 + val_sizes.1, + total_data_sizes.2 + val_sizes.2, + ); + self.raise_err_if_over_data_size_limit( + total_data_sizes, + item_expr.position(), + )?; + } - array.push(value); - } + array.push(value); - result.map(|_| array.into()) + Ok(array) + }, + ) + .map(Into::into) } #[cfg(not(feature = "no_object"))] Expr::Map(x, ..) => { - let mut map = x.1.clone(); - let mut result = Ok(Dynamic::UNIT); - #[cfg(not(feature = "unchecked"))] let mut total_data_sizes = (0, 0, 0); - for (key, value_expr) in &x.0 { - let value = match self - .eval_expr(scope, global, caches, lib, this_ptr, value_expr, level) - { - Ok(r) => r.flatten(), - err => { - result = err; - break; + x.0.iter() + .try_fold(x.1.clone(), |mut map, (key, value_expr)| { + let value = self + .eval_expr(scope, global, caches, lib, this_ptr, value_expr, level)? + .flatten(); + + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + let delta = Self::calc_data_sizes(&value, true); + total_data_sizes = ( + total_data_sizes.0 + delta.0, + total_data_sizes.1 + delta.1, + total_data_sizes.2 + delta.2, + ); + self.raise_err_if_over_data_size_limit( + total_data_sizes, + value_expr.position(), + )?; } - }; - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - let delta = Self::calc_data_sizes(&value, true); - total_data_sizes = ( - total_data_sizes.0 + delta.0, - total_data_sizes.1 + delta.1, - total_data_sizes.2 + delta.2, - ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - value_expr.position(), - )?; - } + *map.get_mut(key.as_str()).unwrap() = value; - *map.get_mut(key.as_str()).unwrap() = value; - } - - result.map(|_| map.into()) + Ok(map) + }) + .map(Into::into) } Expr::And(x, ..) => { @@ -498,17 +477,17 @@ impl Engine { }) }); - if let Ok(true) = lhs { - self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + match lhs { + Ok(true) => self + .eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) .and_then(|v| { v.as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, x.rhs.position()) }) .map(Into::into) - }) - } else { - lhs.map(Into::into) + }), + _ => lhs.map(Into::into), } } @@ -521,17 +500,17 @@ impl Engine { }) }); - if let Ok(false) = lhs { - self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + match lhs { + Ok(false) => self + .eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) .and_then(|v| { v.as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, x.rhs.position()) }) .map(Into::into) - }) - } else { - lhs.map(Into::into) + }), + _ => lhs.map(Into::into), } } @@ -542,7 +521,7 @@ impl Engine { Ok(value) if value.is::<()>() => { self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) } - Ok(_) | Err(_) => lhs, + _ => lhs, } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 77e39dea..387b434e 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -48,13 +48,11 @@ impl Engine { global.scope_level += 1; } - let mut result = Ok(Dynamic::UNIT); - - for stmt in statements { + let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { #[cfg(not(feature = "no_module"))] let imports_len = global.num_imports(); - result = self.eval_stmt( + let result = self.eval_stmt( scope, global, caches, @@ -63,11 +61,7 @@ impl Engine { stmt, restore_orig_state, level, - ); - - if result.is_err() { - break; - } + )?; #[cfg(not(feature = "no_module"))] if matches!(stmt, Stmt::Import(..)) { @@ -92,7 +86,9 @@ impl Engine { } } } - } + + Ok(result) + }); // If imports list is modified, pop the functions lookup cache caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); @@ -429,16 +425,11 @@ impl Engine { }; match cond_result { - Ok(true) => { - result = Ok(Some(&block.expr)); - break; - } - Ok(false) => (), - _ => { - result = cond_result.map(|_| None); - break; - } + Ok(true) => result = Ok(Some(&block.expr)), + Ok(false) => continue, + _ => result = cond_result.map(|_| None), } + break; } result @@ -469,7 +460,6 @@ impl Engine { Ok(false) => continue, _ => result = cond_result.map(|_| None), } - break; } @@ -626,68 +616,58 @@ impl Engine { scope.push(var_name.name.clone(), ()); let index = scope.len() - 1; - let mut loop_result = Ok(Dynamic::UNIT); + let loop_result = func(iter_obj) + .enumerate() + .try_for_each(|(x, iter_value)| { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; - for (x, iter_value) in func(iter_obj).enumerate() { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - loop_result = Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - break; - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => { - loop_result = Err(err.fill_position(expr.position())); - break; - } - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - if let Err(err) = self.track_operation(global, statements.position()) { - loop_result = Err(err); - break; - } - - if statements.is_empty() { - continue; - } - - let result = self.eval_stmt_block( - scope, global, caches, lib, this_ptr, statements, true, level, - ); - - match result { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, ..) => break, - _ => { - loop_result = Err(err); - break; + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); } - }, - } - } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + let value = match iter_value { + Ok(v) => v.flatten(), + Err(err) => return Err(err.fill_position(expr.position())), + }; + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + return Ok(()); + } + + self.eval_stmt_block( + scope, global, caches, lib, this_ptr, statements, true, level, + ) + .map(|_| ()) + .or_else(|err| match *err { + ERR::LoopBreak(false, ..) => Ok(()), + _ => Err(err), + }) + }) + .or_else(|err| match *err { + ERR::LoopBreak(true, ..) => Ok(()), + _ => Err(err), + }); scope.rewind(orig_scope_len); - loop_result + loop_result.map(|_| Dynamic::UNIT) } else { Err(ERR::ErrorFor(expr.start_position()).into()) } From d49dca8a297b723d3355cecf4e70e0452057d569 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 18 Oct 2022 16:54:26 +0800 Subject: [PATCH 048/116] Fix test. --- src/eval/stmt.rs | 150 +++++++++++++++++++++++------------------------ tests/time.rs | 3 +- 2 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 387b434e..c3588754 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -358,24 +358,14 @@ impl Engine { }); match guard_val { - Ok(true) => { - if if_block.is_empty() { - Ok(Dynamic::UNIT) - } else { - self.eval_stmt_block( - scope, global, caches, lib, this_ptr, if_block, true, level, - ) - } - } - Ok(false) => { - if else_block.is_empty() { - Ok(Dynamic::UNIT) - } else { - self.eval_stmt_block( - scope, global, caches, lib, this_ptr, else_block, true, level, - ) - } - } + Ok(true) if if_block.is_empty() => Ok(Dynamic::UNIT), + Ok(true) => self.eval_stmt_block( + scope, global, caches, lib, this_ptr, if_block, true, level, + ), + Ok(false) if else_block.is_empty() => Ok(Dynamic::UNIT), + Ok(false) => self.eval_stmt_block( + scope, global, caches, lib, this_ptr, else_block, true, level, + ), err => err.map(Into::into), } } @@ -490,41 +480,15 @@ impl Engine { } // Loop - Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..)) => loop { + Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..) | Expr::BoolConstant(true, ..)) => { let (.., body) = &**x; if body.is_empty() { - self.track_operation(global, body.position())?; - } else { - match self - .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) - { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), - _ => break Err(err), - }, + loop { + self.track_operation(global, body.position())?; } - } - }, - - // While loop - Stmt::While(x, ..) => loop { - let (expr, body) = &**x; - - let condition = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); - - match condition { - Ok(false) => break Ok(Dynamic::UNIT), - Ok(true) if body.is_empty() => (), - Ok(true) => { + } else { + loop { match self.eval_stmt_block( scope, global, caches, lib, this_ptr, body, true, level, ) { @@ -536,42 +500,76 @@ impl Engine { }, } } - err => break err.map(|_| Dynamic::UNIT), } - }, + } + + // While loop + Stmt::While(x, ..) => { + let (expr, body) = &**x; + + loop { + let condition = self + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .and_then(|v| { + v.as_bool().map_err(|typ| { + self.make_type_mismatch_err::(typ, expr.position()) + }) + }); + + match condition { + Ok(false) => break Ok(Dynamic::UNIT), + Ok(true) if body.is_empty() => (), + Ok(true) => { + match self.eval_stmt_block( + scope, global, caches, lib, this_ptr, body, true, level, + ) { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + _ => break Err(err), + }, + } + } + err => break err.map(|_| Dynamic::UNIT), + } + } + } // Do loop - Stmt::Do(x, options, ..) => loop { + Stmt::Do(x, options, ..) => { let (expr, body) = &**x; let is_while = !options.contains(ASTFlags::NEGATED); - if !body.is_empty() { - match self - .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) - { + loop { + if !body.is_empty() { + match self.eval_stmt_block( + scope, global, caches, lib, this_ptr, body, true, level, + ) { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => continue, + ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + _ => break Err(err), + }, + } + } + + let condition = self + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .and_then(|v| { + v.as_bool().map_err(|typ| { + self.make_type_mismatch_err::(typ, expr.position()) + }) + }); + + match condition { + Ok(condition) if condition ^ is_while => break Ok(Dynamic::UNIT), Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => continue, - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), - _ => break Err(err), - }, + err => break err.map(|_| Dynamic::UNIT), } } - - let condition = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); - - match condition { - Ok(condition) if condition ^ is_while => break Ok(Dynamic::UNIT), - Ok(_) => (), - err => break err.map(|_| Dynamic::UNIT), - } - }, + } // For loop Stmt::For(x, ..) => { diff --git a/tests/time.rs b/tests/time.rs index 7a35da15..5434faa5 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -1,5 +1,4 @@ -#![cfg(not(feature = "no_std"))] -#![cfg(not(target_family = "wasm"))] +#![cfg(not(feature = "no_time"))] use rhai::{Engine, EvalAltResult}; From 3073f815ec0749e9417078f549680b52e9320264 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 19 Oct 2022 12:51:52 +0800 Subject: [PATCH 049/116] Add unsafe handle test. --- tests/plugins.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/plugins.rs b/tests/plugins.rs index 5fe58fdc..dfb0c9c5 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -2,10 +2,10 @@ #![cfg(not(feature = "no_module"))] use rhai::plugin::*; -use rhai::{Engine, EvalAltResult, INT}; +use rhai::{Engine, EvalAltResult, Scope, INT}; mod test { - use rhai::plugin::*; + use super::*; #[export_module] pub mod special_array_package { @@ -170,3 +170,56 @@ fn test_plugins_parameters() -> Result<(), Box> { Ok(()) } + +#[cfg(target_pointer_width = "64")] +mod handle { + use super::*; + + #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] + pub struct WorldHandle(usize); + pub type World = Vec; + + impl From<&mut World> for WorldHandle { + fn from(world: &mut World) -> Self { + Self::new(world) + } + } + + impl AsMut for WorldHandle { + fn as_mut(&mut self) -> &mut World { + unsafe { std::mem::transmute(self.0) } + } + } + + impl WorldHandle { + pub fn new(world: &mut World) -> Self { + Self(unsafe { std::mem::transmute(world) }) + } + } + + #[export_module] + pub mod handle_module { + pub type Handle = WorldHandle; + + #[rhai_fn(get = "len")] + pub fn len(world: &mut Handle) -> INT { + world.as_mut().len() as INT + } + } + + #[test] + fn test_module_handle() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine.register_global_module(exported_module!(handle_module).into()); + + let mut scope = Scope::new(); + + let world: &mut World = &mut vec![42]; + scope.push("world", WorldHandle::from(world)); + + assert_eq!(engine.eval_with_scope::(&mut scope, "world.len")?, 1); + + Ok(()) + } +} From 9f36afc84cdb9a711247a9877a0081393b9269a6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 09:53:11 +0800 Subject: [PATCH 050/116] Check result size in op assignment. --- src/eval/stmt.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index c3588754..fac3c749 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -160,7 +160,7 @@ impl Engine { match self.call_native_fn( global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { - Ok(_) => self.check_data_size(args[0], root.1)?, + Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` @@ -174,6 +174,8 @@ impl Engine { } Err(err) => return Err(err), } + + self.check_data_size(args[0], root.1)?; } else { // Normal assignment *target.write_lock::().unwrap() = new_val; From 988222418eff98f29645307d67dbd409bfd9376f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 11:38:54 +0800 Subject: [PATCH 051/116] Check data size at built-in. --- src/eval/data_check.rs | 16 ++- src/eval/expr.rs | 15 +-- src/eval/stmt.rs | 6 +- src/func/builtin.rs | 223 +++++++++++++++++++++++++++++------- src/packages/array_basic.rs | 2 +- src/packages/blob_basic.rs | 8 +- 6 files changed, 202 insertions(+), 68 deletions(-) diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index bc8953ca..9195f632 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -73,14 +73,15 @@ impl Engine { pub(crate) fn raise_err_if_over_data_size_limit( &self, (_arr, _map, s): (usize, usize, usize), - pos: Position, ) -> RhaiResultOf<()> { if self .limits .max_string_size .map_or(false, |max| s > max.get()) { - return Err(ERR::ErrorDataTooLarge("Length of string".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_index"))] @@ -89,7 +90,9 @@ impl Engine { .max_array_size .map_or(false, |max| _arr > max.get()) { - return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Size of array/BLOB".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_object"))] @@ -98,7 +101,9 @@ impl Engine { .max_map_size .map_or(false, |max| _map > max.get()) { - return Err(ERR::ErrorDataTooLarge("Size of object map".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(), + ); } Ok(()) @@ -113,7 +118,8 @@ impl Engine { let sizes = Self::calc_data_sizes(value, true); - self.raise_err_if_over_data_size_limit(sizes, pos) + self.raise_err_if_over_data_size_limit(sizes) + .map_err(|err| err.fill_position(pos)) } /// Raise an error if the size of a [`Dynamic`] is out of limits (if any). diff --git a/src/eval/expr.rs b/src/eval/expr.rs index d86c3da6..285f5e23 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -252,8 +252,7 @@ impl Engine { { // Built-in found let context = (self, name, None, &*global, lib, pos, level + 1).into(); - let result = func(context, operands); - return self.check_return_value(result, pos); + return func(context, operands); } return self @@ -422,10 +421,8 @@ impl Engine { total_data_sizes.1 + val_sizes.1, total_data_sizes.2 + val_sizes.2, ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - item_expr.position(), - )?; + self.raise_err_if_over_data_size_limit(total_data_sizes) + .map_err(|err| err.fill_position(item_expr.position()))?; } array.push(value); @@ -455,10 +452,8 @@ impl Engine { total_data_sizes.1 + delta.1, total_data_sizes.2 + delta.2, ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - value_expr.position(), - )?; + self.raise_err_if_over_data_size_limit(total_data_sizes) + .map_err(|err| err.fill_position(value_expr.position()))?; } *map.get_mut(key.as_str()).unwrap() = value; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index fac3c749..4e26d7d3 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -146,11 +146,7 @@ impl Engine { // Built-in found let op = op_assign.literal_syntax(); let context = (self, op, None, &*global, lib, *op_pos, level).into(); - let result = func(context, args).map(|_| ()); - - self.check_data_size(args[0], root.1)?; - - return result; + return func(context, args).map(|_| ()); } } diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 6b16366f..0803d35e 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -196,7 +196,19 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::Plus => Some(impl_op!(ImmutableString + ImmutableString)), + Token::Plus => Some(|_ctx, args| { + let s1 = &*args[0].read_lock::().expect(BUILTIN); + let s2 = &*args[1].read_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + if !s1.is_empty() && !s2.is_empty() { + let total_len = s1.len() + s2.len(); + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, total_len))?; + } + + Ok((s1 + s2).into()) + }), Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), @@ -210,10 +222,17 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); - Ok(format!("{x}{y}").into()) + + let result = format!("{x}{y}"); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), @@ -230,7 +249,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< use crate::Blob; return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -239,6 +258,13 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } else if blob1.is_empty() { blob2.clone() } else { + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob1.len() + blob2.len(), + 0, + 0, + ))?; + let mut blob = blob1.clone(); blob.extend(blob2); blob @@ -357,10 +383,16 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); - Ok(format!("{x}{y}").into()) + let result = format!("{x}{y}"); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::EqualsTo => Some(impl_op!(get_s1s2(==))), Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), @@ -383,10 +415,16 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); - Ok((x + y).into()) + let result = x + y; + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); @@ -436,10 +474,18 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type2 == TypeId::of::() { return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let mut buf = [0_u8; 4]; let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); - let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf); + let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob.len() + x.len(), + 0, + 0, + ))?; + blob.extend(x.as_bytes()); Ok(Dynamic::from_blob(blob)) }), @@ -638,10 +684,18 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); + + #[cfg(not(feature = "unchecked"))] + if !x.is_empty() && !y.is_empty() { + let total_len = x.len() + y.len(); + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, total_len))?; + } + Ok((*x += y).into()) }), Token::MinusAssign => Some(|_, args| { @@ -654,14 +708,56 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt }; } + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::packages::array_basic::array_functions::*; + use crate::Array; + + return match op { + Token::PlusAssign => Some(|_ctx, args| { + let x = std::mem::take(args[1]).cast::(); + + if x.is_empty() { + return Ok(Dynamic::UNIT); + } + + let _array_was_empty = { + let array = &mut &mut *args[0].write_lock::().expect(BUILTIN); + let array_is_empty = array.is_empty(); + append(array, x); + array_is_empty + }; + + #[cfg(not(feature = "unchecked"))] + if !_array_was_empty { + _ctx.engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; + } + + Ok(Dynamic::UNIT) + }), + _ => None, + }; + } + #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { use crate::Blob; return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let blob2 = std::mem::take(args[1]).cast::(); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob1.len() + blob2.len(), + 0, + 0, + ))?; + Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) }), _ => None, @@ -736,7 +832,17 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(impl_op!(ImmutableString += as_char as char)), + Token::PlusAssign => Some(|_ctx, args| { + let mut buf = [0_u8; 4]; + let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); + let mut x = args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, x.len() + ch.len()))?; + + Ok((*x += ch).into()) + }), Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; @@ -744,17 +850,27 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let mut ch = args[0].as_char().expect(BUILTIN).to_string(); - ch.push_str( - args[1] - .read_lock::() - .expect(BUILTIN) - .as_str(), - ); + Token::PlusAssign => Some(|_ctx, args| { + let ch = { + let s = &*args[1].read_lock::().expect(BUILTIN); - let mut x = args[0].write_lock::().expect(BUILTIN); - Ok((*x = ch.into()).into()) + if s.is_empty() { + return Ok(Dynamic::UNIT); + } + + let mut ch = args[0].as_char().expect(BUILTIN).to_string(); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, ch.len() + s.len()))?; + + ch.push_str(s); + ch + }; + + *args[0].write_lock::().expect(BUILTIN) = ch.into(); + + Ok(Dynamic::UNIT) }), _ => None, }; @@ -766,21 +882,21 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::packages::array_basic::array_functions::*; use crate::Array; - if type2 == TypeId::of::() { - return match op { - Token::PlusAssign => Some(|_, args| { - let array2 = std::mem::take(args[1]).cast::(); - let array1 = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(append(array1, array2).into()) - }), - _ => None, - }; - } return match op { - Token::PlusAssign => Some(|_, args| { - let x = std::mem::take(args[1]); - let array = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(push(array, x).into()) + Token::PlusAssign => Some(|_ctx, args| { + { + let x = std::mem::take(args[1]); + let array = &mut *args[0].write_lock::().expect(BUILTIN); + push(array, x); + } + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; + + Ok(Dynamic::UNIT) }), _ => None, }; @@ -793,9 +909,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let x = args[1].as_int().expect("`INT`"); + Token::PlusAssign => Some(|_ctx, args| { + let x = args[1].as_int().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; + Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) }), _ => None, @@ -805,9 +926,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let x = args[1].as_char().expect("`char`"); + Token::PlusAssign => Some(|_ctx, args| { + let x = args[1].as_char().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; + Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) }), _ => None, @@ -817,9 +943,22 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let s = std::mem::take(args[1]).cast::(); + + if s.is_empty() { + return Ok(Dynamic::UNIT); + } + let blob = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob.len() + s.len(), + 0, + 0, + ))?; + Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) }), _ => None, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 2c5bd716..098b7f7d 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -259,7 +259,7 @@ pub mod array_functions { s1 += s2; _ctx.engine() - .raise_err_if_over_data_size_limit((a1, m1, s1), Position::NONE)?; + .raise_err_if_over_data_size_limit((a1, m1, s1))?; guard.push(item.clone()); arr_len += 1; diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 4ddd68b6..2e51b108 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -79,11 +79,9 @@ pub mod blob_functions { let _ctx = ctx; // Check if blob will be over max size limit - if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err( - crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), - ); - } + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((len, 0, 0))?; let mut blob = Blob::new(); blob.resize(len, (value & 0x0000_00ff) as u8); From f8888c83e7c0d320e46c7eef4a9f0d8abca8cc31 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 13:01:36 +0800 Subject: [PATCH 052/116] Fix test. --- tests/plugins.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/plugins.rs b/tests/plugins.rs index dfb0c9c5..f5c8d966 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -218,6 +218,7 @@ mod handle { let world: &mut World = &mut vec![42]; scope.push("world", WorldHandle::from(world)); + #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval_with_scope::(&mut scope, "world.len")?, 1); Ok(()) From c24794187f745cd0ee2c086bc8d36ceaa4819845 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 15:31:57 +0800 Subject: [PATCH 053/116] Reduce unnecessary generics. --- src/api/custom_syntax.rs | 10 ++++++++- src/ast/ast.rs | 27 +++++++++++++++++++++++ src/ast/ident.rs | 9 ++++++++ src/ast/stmt.rs | 9 ++++++++ src/eval/expr.rs | 2 +- src/eval/target.rs | 13 +++++++++++- src/func/native.rs | 46 +++++++++++++++++----------------------- src/optimizer.rs | 2 +- 8 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 7ee568c0..868b9931 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -10,9 +10,9 @@ use crate::{ reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult, StaticVec, }; -use std::ops::Deref; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{borrow::Borrow, ops::Deref}; /// Collection of special markers for custom syntax definition. pub mod markers { @@ -145,6 +145,14 @@ impl Expression<'_> { } } +impl Borrow for Expression<'_> { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &Expr { + self.0 + } +} + impl AsRef for Expression<'_> { #[inline(always)] #[must_use] diff --git a/src/ast/ast.rs b/src/ast/ast.rs index d0b88e26..98ed86b3 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -5,6 +5,7 @@ use crate::{Dynamic, FnNamespace, Identifier, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + borrow::Borrow, fmt, hash::Hash, ops::{Add, AddAssign}, @@ -919,6 +920,14 @@ impl> AddAssign for AST { } } +impl Borrow<[Stmt]> for AST { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &[Stmt] { + self.statements() + } +} + impl AsRef<[Stmt]> for AST { #[inline(always)] #[must_use] @@ -927,6 +936,15 @@ impl AsRef<[Stmt]> for AST { } } +#[cfg(not(feature = "no_function"))] +impl Borrow for AST { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &crate::Module { + &self.shared_lib() + } +} + #[cfg(not(feature = "no_function"))] impl AsRef for AST { #[inline(always)] @@ -936,6 +954,15 @@ impl AsRef for AST { } } +#[cfg(not(feature = "no_function"))] +impl Borrow> for AST { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &crate::Shared { + self.shared_lib() + } +} + #[cfg(not(feature = "no_function"))] impl AsRef> for AST { #[inline(always)] diff --git a/src/ast/ident.rs b/src/ast/ident.rs index e8eb6626..ffc5f968 100644 --- a/src/ast/ident.rs +++ b/src/ast/ident.rs @@ -4,6 +4,7 @@ use crate::{ImmutableString, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + borrow::Borrow, fmt, hash::Hash, ops::{Deref, DerefMut}, @@ -28,6 +29,14 @@ impl fmt::Debug for Ident { } } +impl Borrow for Ident { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &str { + self.name.as_ref() + } +} + impl AsRef for Ident { #[inline(always)] #[must_use] diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index a72e2f01..6b15061c 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -7,6 +7,7 @@ use crate::{calc_fn_hash, Position, StaticVec, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + borrow::Borrow, collections::BTreeMap, fmt, hash::Hash, @@ -443,6 +444,14 @@ impl DerefMut for StmtBlock { } } +impl Borrow<[Stmt]> for StmtBlock { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &[Stmt] { + &self.block + } +} + impl AsRef<[Stmt]> for StmtBlock { #[inline(always)] #[must_use] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 285f5e23..c8d17896 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -251,7 +251,7 @@ impl Engine { get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1]) { // Built-in found - let context = (self, name, None, &*global, lib, pos, level + 1).into(); + let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); return func(context, operands); } diff --git a/src/eval/target.rs b/src/eval/target.rs index 5a441520..c48aa62a 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -2,9 +2,12 @@ use crate::types::dynamic::Variant; use crate::{Dynamic, Position, RhaiResultOf}; -use std::ops::{Deref, DerefMut}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{ + borrow::Borrow, + ops::{Deref, DerefMut}, +}; // Calculate an offset+len pair given an actual length of the underlying array. // @@ -422,6 +425,14 @@ impl AsRef for Target<'_> { } } +impl Borrow for Target<'_> { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &Dynamic { + self + } +} + impl DerefMut for Target<'_> { #[inline] fn deref_mut(&mut self) -> &mut Dynamic { diff --git a/src/func/native.rs b/src/func/native.rs index b3681e74..707f8f4c 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -81,13 +81,13 @@ pub struct NativeCallContext<'a> { level: usize, } -impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> +impl<'a> From<( &'a Engine, - &'a S, - Option<&'a S>, + &'a str, + Option<&'a str>, &'a GlobalRuntimeState<'a>, - &'a M, + &'a [&Module], Position, usize, )> for NativeCallContext<'a> @@ -96,37 +96,35 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> fn from( value: ( &'a Engine, - &'a S, - Option<&'a S>, + &'a str, + Option<&'a str>, &'a GlobalRuntimeState, - &'a M, + &'a [&Module], Position, usize, ), ) -> Self { Self { engine: value.0, - fn_name: value.1.as_ref(), - source: value.2.map(<_>::as_ref), + fn_name: value.1, + source: value.2, global: Some(value.3), - lib: value.4.as_ref(), + lib: value.4, pos: value.5, level: value.6, } } } -impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> - From<(&'a Engine, &'a S, &'a M)> for NativeCallContext<'a> -{ +impl<'a> From<(&'a Engine, &'a str, &'a [&'a Module])> for NativeCallContext<'a> { #[inline(always)] - fn from(value: (&'a Engine, &'a S, &'a M)) -> Self { + fn from(value: (&'a Engine, &'a str, &'a [&Module])) -> Self { Self { engine: value.0, - fn_name: value.1.as_ref(), + fn_name: value.1, source: None, global: None, - lib: value.2.as_ref(), + lib: value.2, pos: Position::NONE, level: 0, } @@ -142,14 +140,10 @@ impl<'a> NativeCallContext<'a> { )] #[inline(always)] #[must_use] - pub fn new( - engine: &'a Engine, - fn_name: &'a (impl AsRef + 'a + ?Sized), - lib: &'a [&Module], - ) -> Self { + pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { Self { engine, - fn_name: fn_name.as_ref(), + fn_name, source: None, global: None, lib, @@ -167,8 +161,8 @@ impl<'a> NativeCallContext<'a> { #[must_use] pub fn new_with_all_fields( engine: &'a Engine, - fn_name: &'a (impl AsRef + 'a + ?Sized), - source: Option<&'a (impl AsRef + 'a + ?Sized)>, + fn_name: &'a str, + source: Option<&'a str>, global: &'a GlobalRuntimeState, lib: &'a [&Module], pos: Position, @@ -176,8 +170,8 @@ impl<'a> NativeCallContext<'a> { ) -> Self { Self { engine, - fn_name: fn_name.as_ref(), - source: source.map(<_>::as_ref), + fn_name, + source, global: Some(global), lib, pos, diff --git a/src/optimizer.rs b/src/optimizer.rs index 20145659..42e9dd90 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1191,7 +1191,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(feature = "no_function")] let lib = &[]; - let context = (state.engine, &x.name, lib).into(); + let context = (state.engine, x.name.as_str(), lib).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { From 2f88757efebf52cb1c4f3bea76e00e8cfcf1ef4b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 21 Oct 2022 19:02:40 +0800 Subject: [PATCH 054/116] Fix builds. --- src/optimizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimizer.rs b/src/optimizer.rs index 42e9dd90..bd51a7c4 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1189,7 +1189,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(not(feature = "no_function"))] let lib = state.lib; #[cfg(feature = "no_function")] - let lib = &[]; + let lib = &[][..]; let context = (state.engine, x.name.as_str(), lib).into(); let (first, second) = arg_values.split_first_mut().unwrap(); From d55b5a91f7b0595324440dc47a61d3f8153ac9d1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 24 Oct 2022 23:33:24 +0800 Subject: [PATCH 055/116] Fill variable index on captured variables in closures. --- src/parser.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 6c100008..cb002908 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1441,7 +1441,7 @@ impl Engine { ..settings }; - let result = self.parse_anon_fn(input, &mut new_state, lib, new_settings); + let result = self.parse_anon_fn(input, &mut new_state, state, lib, new_settings); // Restore parse state state.interned_strings = new_state.interned_strings; @@ -1464,7 +1464,7 @@ impl Engine { // Under Strict Variables mode, this is not allowed. Err(PERR::VariableUndefined(name.to_string()).into_err(*pos)) } else { - Ok::<_, ParseError>(()) + Ok(()) } }, )?; @@ -3655,6 +3655,8 @@ impl Engine { #[cfg(not(feature = "no_closure"))] fn make_curry_from_externals( state: &mut ParseState, + parent: &mut ParseState, + lib: &FnLib, fn_expr: Expr, externals: StaticVec, pos: Position, @@ -3674,12 +3676,14 @@ impl Engine { .iter() .cloned() .map(|crate::ast::Ident { name, pos }| { - #[cfg(not(feature = "no_module"))] - let ns = crate::ast::Namespace::NONE; - #[cfg(feature = "no_module")] - let ns = (); - - Expr::Variable((None, ns, 0, name).into(), None, pos) + let (index, is_func) = parent.access_var(&name, lib, pos); + let idx = match index { + Some(n) if !is_func && n.get() <= u8::MAX as usize => { + NonZeroU8::new(n.get() as u8) + } + _ => None, + }; + Expr::Variable((index, Default::default(), 0, name).into(), idx, pos) }), ); @@ -3719,6 +3723,7 @@ impl Engine { &self, input: &mut TokenStream, state: &mut ParseState, + parent: &mut ParseState, lib: &mut FnLib, settings: ParseSettings, ) -> ParseResult<(Expr, ScriptFnDef)> { @@ -3815,7 +3820,8 @@ impl Engine { let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos); #[cfg(not(feature = "no_closure"))] - let expr = Self::make_curry_from_externals(state, expr, externals, settings.pos); + let expr = + Self::make_curry_from_externals(state, parent, lib, expr, externals, settings.pos); Ok((expr, script)) } From 05b9c83bb889e789e20db0424aedc4d4036443af Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 24 Oct 2022 23:33:33 +0800 Subject: [PATCH 056/116] Reformat. --- src/func/builtin.rs | 433 ++++++++++++++++++++++---------------------- 1 file changed, 220 insertions(+), 213 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 0803d35e..8ff37e67 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -2,8 +2,10 @@ use super::call::FnCallArgs; use super::native::FnBuiltin; -use crate::tokenizer::Token; -use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT}; +use crate::tokenizer::{Token, Token::*}; +use crate::{ + Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT, +}; use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -63,6 +65,19 @@ fn is_numeric(type_id: TypeId) -> bool { false } +/// A function that returns `true`. +#[inline(always)] +#[must_use] +fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { + Ok(Dynamic::TRUE) +} +/// A function that returns `false`. +#[inline(always)] +#[must_use] +fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { + Ok(Dynamic::FALSE) +} + /// Build in common binary operator implementations to avoid the cost of calling a registered function. /// /// The return function will be registered as a _method_, so the first parameter cannot be consumed. @@ -131,46 +146,46 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< #[cfg(not(feature = "unchecked"))] match op { - Token::Plus => return Some(impl_op!(INT => add(as_int, as_int))), - Token::Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), - Token::Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), - Token::Divide => return Some(impl_op!(INT => divide(as_int, as_int))), - Token::Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), - Token::PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), - Token::RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))), - Token::LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))), + Plus => return Some(impl_op!(INT => add(as_int, as_int))), + Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), + Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), + Divide => return Some(impl_op!(INT => divide(as_int, as_int))), + Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), + PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), + RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))), + LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - Token::Plus => return Some(impl_op!(INT => as_int + as_int)), - Token::Minus => return Some(impl_op!(INT => as_int - as_int)), - Token::Multiply => return Some(impl_op!(INT => as_int * as_int)), - Token::Divide => return Some(impl_op!(INT => as_int / as_int)), - Token::Modulo => return Some(impl_op!(INT => as_int % as_int)), - Token::PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - Token::RightShift => return Some(impl_op!(INT => as_int >> as_int)), - Token::LeftShift => return Some(impl_op!(INT => as_int << as_int)), + Plus => return Some(impl_op!(INT => as_int + as_int)), + Minus => return Some(impl_op!(INT => as_int - as_int)), + Multiply => return Some(impl_op!(INT => as_int * as_int)), + Divide => return Some(impl_op!(INT => as_int / as_int)), + Modulo => return Some(impl_op!(INT => as_int % as_int)), + PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + RightShift => return Some(impl_op!(INT => as_int >> as_int)), + LeftShift => return Some(impl_op!(INT => as_int << as_int)), _ => (), } return match op { - Token::EqualsTo => Some(impl_op!(INT => as_int == as_int)), - Token::NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), - Token::GreaterThan => Some(impl_op!(INT => as_int > as_int)), - Token::GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), - Token::LessThan => Some(impl_op!(INT => as_int < as_int)), - Token::LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), - Token::Ampersand => Some(impl_op!(INT => as_int & as_int)), - Token::Pipe => Some(impl_op!(INT => as_int | as_int)), - Token::XOr => Some(impl_op!(INT => as_int ^ as_int)), - Token::ExclusiveRange => Some(|_, args| { + EqualsTo => Some(impl_op!(INT => as_int == as_int)), + NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), + GreaterThan => Some(impl_op!(INT => as_int > as_int)), + GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), + LessThan => Some(impl_op!(INT => as_int < as_int)), + LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), + Ampersand => Some(impl_op!(INT => as_int & as_int)), + Pipe => Some(impl_op!(INT => as_int | as_int)), + XOr => Some(impl_op!(INT => as_int ^ as_int)), + ExclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..y).into()) }), - Token::InclusiveRange => Some(|_, args| { + InclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..=y).into()) @@ -181,22 +196,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), - Token::NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), - Token::GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), - Token::GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), - Token::LessThan => Some(impl_op!(bool => as_bool < as_bool)), - Token::LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), - Token::Ampersand => Some(impl_op!(bool => as_bool & as_bool)), - Token::Pipe => Some(impl_op!(bool => as_bool | as_bool)), - Token::XOr => Some(impl_op!(bool => as_bool ^ as_bool)), + EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), + NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), + GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), + GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), + LessThan => Some(impl_op!(bool => as_bool < as_bool)), + LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), + Ampersand => Some(impl_op!(bool => as_bool & as_bool)), + Pipe => Some(impl_op!(bool => as_bool | as_bool)), + XOr => Some(impl_op!(bool => as_bool ^ as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let s1 = &*args[0].read_lock::().expect(BUILTIN); let s2 = &*args[1].read_lock::().expect(BUILTIN); @@ -209,20 +224,20 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok((s1 + s2).into()) }), - Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), - Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), - Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), - Token::GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), - Token::GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), - Token::LessThan => Some(impl_op!(ImmutableString < ImmutableString)), - Token::LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), + Minus => Some(impl_op!(ImmutableString - ImmutableString)), + EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), + NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), + GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), + GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), + LessThan => Some(impl_op!(ImmutableString < ImmutableString)), + LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); @@ -234,12 +249,12 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok(result.into()) }), - Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), - Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), - Token::GreaterThan => Some(impl_op!(char => as_char > as_char)), - Token::GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), - Token::LessThan => Some(impl_op!(char => as_char < as_char)), - Token::LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), + EqualsTo => Some(impl_op!(char => as_char == as_char)), + NotEqualsTo => Some(impl_op!(char => as_char != as_char)), + GreaterThan => Some(impl_op!(char => as_char > as_char)), + GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), + LessThan => Some(impl_op!(char => as_char < as_char)), + LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), _ => None, }; } @@ -249,7 +264,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< use crate::Blob; return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -270,20 +285,18 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< blob })) }), - Token::EqualsTo => Some(impl_op!(Blob == Blob)), - Token::NotEqualsTo => Some(impl_op!(Blob != Blob)), + EqualsTo => Some(impl_op!(Blob == Blob)), + NotEqualsTo => Some(impl_op!(Blob != Blob)), _ => None, }; } if type1 == TypeId::of::<()>() { return match op { - Token::EqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::NotEqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + EqualsTo => Some(const_true_fn), + NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } _ => None, }; } @@ -294,19 +307,19 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< ($x:ty, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - Token::Plus => Some(impl_op!(FLOAT => $xx + $yy)), - Token::Minus => Some(impl_op!(FLOAT => $xx - $yy)), - Token::Multiply => Some(impl_op!(FLOAT => $xx * $yy)), - Token::Divide => Some(impl_op!(FLOAT => $xx / $yy)), - Token::Modulo => Some(impl_op!(FLOAT => $xx % $yy)), - Token::PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), - Token::EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), - Token::NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), - Token::GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), - Token::GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), - Token::LessThan => Some(impl_op!(FLOAT => $xx < $yy)), - Token::LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), - _ => None, + Plus => Some(impl_op!(FLOAT => $xx + $yy)), + Minus => Some(impl_op!(FLOAT => $xx - $yy)), + Multiply => Some(impl_op!(FLOAT => $xx * $yy)), + Divide => Some(impl_op!(FLOAT => $xx / $yy)), + Modulo => Some(impl_op!(FLOAT => $xx % $yy)), + PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), + EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), + NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), + GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), + GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), + LessThan => Some(impl_op!(FLOAT => $xx < $yy)), + LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), + _ => None, }; } }; @@ -328,13 +341,13 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< #[cfg(not(feature = "unchecked"))] match op { - Token::Plus => return Some(impl_op!(from Decimal => add($xx, $yy))), - Token::Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), - Token::Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), - Token::Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), - Token::Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), - Token::PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), - _ => () + Plus => return Some(impl_op!(from Decimal => add($xx, $yy))), + Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), + Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), + Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), + Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), + PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), + _ => () } #[cfg(feature = "unchecked")] @@ -342,23 +355,23 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< #[cfg(feature = "unchecked")] match op { - Token::Plus => return Some(impl_op!(from Decimal => $xx + $yy)), - Token::Minus => return Some(impl_op!(from Decimal => $xx - $yy)), - Token::Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), - Token::Divide => return Some(impl_op!(from Decimal => $xx / $yy)), - Token::Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), - Token::PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), - _ => () + Plus => return Some(impl_op!(from Decimal => $xx + $yy)), + Minus => return Some(impl_op!(from Decimal => $xx - $yy)), + Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), + Divide => return Some(impl_op!(from Decimal => $xx / $yy)), + Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), + PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), + _ => () } return match op { - Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), - Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), - Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), - Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), - Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)), - Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), - _ => None + EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), + NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), + GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), + GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), + LessThan => Some(impl_op!(from Decimal => $xx < $yy)), + LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), + _ => None }; } }; @@ -383,7 +396,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); let result = format!("{x}{y}"); @@ -394,12 +407,12 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok(result.into()) }), - Token::EqualsTo => Some(impl_op!(get_s1s2(==))), - Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), - Token::GreaterThan => Some(impl_op!(get_s1s2(>))), - Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), - Token::LessThan => Some(impl_op!(get_s1s2(<))), - Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), + EqualsTo => Some(impl_op!(get_s1s2(==))), + NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + GreaterThan => Some(impl_op!(get_s1s2(>))), + GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + LessThan => Some(impl_op!(get_s1s2(<))), + LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } @@ -415,7 +428,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); let result = x + y; @@ -426,43 +439,39 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok(result.into()) }), - Token::Minus => Some(|_, args| { + Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok((x - y).into()) }), - Token::EqualsTo => Some(impl_op!(get_s1s2(==))), - Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), - Token::GreaterThan => Some(impl_op!(get_s1s2(>))), - Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), - Token::LessThan => Some(impl_op!(get_s1s2(<))), - Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), + EqualsTo => Some(impl_op!(get_s1s2(==))), + NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + GreaterThan => Some(impl_op!(get_s1s2(>))), + GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + LessThan => Some(impl_op!(get_s1s2(<))), + LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } // () op string if (type1, type2) == (TypeId::of::<()>(), TypeId::of::()) { return match op { - Token::Plus => Some(|_, args| Ok(args[1].clone())), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Plus => Some(|_, args| Ok(args[1].clone())), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } + NotEqualsTo => Some(const_true_fn), _ => None, }; } // string op () if (type1, type2) == (TypeId::of::(), TypeId::of::<()>()) { return match op { - Token::Plus => Some(|_, args| Ok(args[0].clone())), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Plus => Some(|_, args| Ok(args[0].clone())), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } + NotEqualsTo => Some(const_true_fn), _ => None, }; } @@ -474,9 +483,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type2 == TypeId::of::() { return match op { - Token::Plus => Some(|_ctx, args| { - let mut buf = [0_u8; 4]; + Plus => Some(|_ctx, args| { let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); + let mut buf = [0_u8; 4]; let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); #[cfg(not(feature = "unchecked"))] @@ -507,8 +516,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< ) { return match op { - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::Equals => Some(|_, _| Ok(Dynamic::FALSE)), + NotEqualsTo => Some(const_true_fn), + Equals => Some(const_false_fn), _ => None, }; } @@ -517,8 +526,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { if type1 == type2 { return match op { - Token::EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), - Token::NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), + EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), + NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), _ => None, }; } @@ -527,8 +536,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { if type1 == type2 { return match op { - Token::EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), - Token::NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), + EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), + NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), _ => None, }; } @@ -542,12 +551,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } else if type1 != type2 { // If the types are not the same, default to not compare match op { - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + NotEqualsTo => Some(const_true_fn), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } _ => None, } } else { @@ -559,12 +566,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< // Default comparison operators for different types if type2 != type1 { return match op { - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + NotEqualsTo => Some(const_true_fn), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } _ => None, }; } @@ -629,51 +634,49 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(not(feature = "unchecked"))] match op { - Token::PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), - Token::MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), - Token::MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), - Token::DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), - Token::ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), - Token::PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), - Token::RightShiftAssign => { - return Some(impl_op!(INT => shift_right(as_int, as_int))) - } - Token::LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))), + PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), + MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), + MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), + DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), + ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), + PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), + RightShiftAssign => return Some(impl_op!(INT => shift_right(as_int, as_int))), + LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - Token::PlusAssign => return Some(impl_op!(INT += as_int)), - Token::MinusAssign => return Some(impl_op!(INT -= as_int)), - Token::MultiplyAssign => return Some(impl_op!(INT *= as_int)), - Token::DivideAssign => return Some(impl_op!(INT /= as_int)), - Token::ModuloAssign => return Some(impl_op!(INT %= as_int)), - Token::PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - Token::RightShiftAssign => return Some(impl_op!(INT >>= as_int)), - Token::LeftShiftAssign => return Some(impl_op!(INT <<= as_int)), + PlusAssign => return Some(impl_op!(INT += as_int)), + MinusAssign => return Some(impl_op!(INT -= as_int)), + MultiplyAssign => return Some(impl_op!(INT *= as_int)), + DivideAssign => return Some(impl_op!(INT /= as_int)), + ModuloAssign => return Some(impl_op!(INT %= as_int)), + PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + RightShiftAssign => return Some(impl_op!(INT >>= as_int)), + LeftShiftAssign => return Some(impl_op!(INT <<= as_int)), _ => (), } return match op { - Token::AndAssign => Some(impl_op!(INT &= as_int)), - Token::OrAssign => Some(impl_op!(INT |= as_int)), - Token::XOrAssign => Some(impl_op!(INT ^= as_int)), + AndAssign => Some(impl_op!(INT &= as_int)), + OrAssign => Some(impl_op!(INT |= as_int)), + XOrAssign => Some(impl_op!(INT ^= as_int)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::AndAssign => Some(impl_op!(bool = x && as_bool)), - Token::OrAssign => Some(impl_op!(bool = x || as_bool)), + AndAssign => Some(impl_op!(bool = x && as_bool)), + OrAssign => Some(impl_op!(bool = x || as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_, args| { + PlusAssign => Some(|_, args| { let y = args[1].as_char().expect(BUILTIN); let x = &mut *args[0].write_lock::().expect(BUILTIN); Ok((*x = format!("{x}{y}").into()).into()) @@ -684,7 +687,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); @@ -698,7 +701,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt Ok((*x += y).into()) }), - Token::MinusAssign => Some(|_, args| { + MinusAssign => Some(|_, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); @@ -714,29 +717,26 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::Array; return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let x = std::mem::take(args[1]).cast::(); if x.is_empty() { return Ok(Dynamic::UNIT); } - let _array_was_empty = { - let array = &mut &mut *args[0].write_lock::().expect(BUILTIN); - let array_is_empty = array.is_empty(); - append(array, x); - array_is_empty - }; + let _array_is_empty = args[0].read_lock::().expect(BUILTIN).is_empty(); #[cfg(not(feature = "unchecked"))] - if !_array_was_empty { + if !_array_is_empty { _ctx.engine().check_data_size( &*args[0].read_lock().expect(BUILTIN), crate::Position::NONE, )?; } - Ok(Dynamic::UNIT) + let array = &mut *args[0].write_lock::().expect(BUILTIN); + + Ok(append(array, x).into()) }), _ => None, }; @@ -744,10 +744,11 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { + use crate::packages::blob_basic::blob_functions::*; use crate::Blob; return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let blob2 = std::mem::take(args[1]).cast::(); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); @@ -758,7 +759,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt 0, ))?; - Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) + Ok(append(blob1, blob2).into()) }), _ => None, }; @@ -770,13 +771,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt ($x:ident, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - Token::PlusAssign => Some(impl_op!($x += $yy)), - Token::MinusAssign => Some(impl_op!($x -= $yy)), - Token::MultiplyAssign => Some(impl_op!($x *= $yy)), - Token::DivideAssign => Some(impl_op!($x /= $yy)), - Token::ModuloAssign => Some(impl_op!($x %= $yy)), - Token::PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), - _ => None, + PlusAssign => Some(impl_op!($x += $yy)), + MinusAssign => Some(impl_op!($x -= $yy)), + MultiplyAssign => Some(impl_op!($x *= $yy)), + DivideAssign => Some(impl_op!($x /= $yy)), + ModuloAssign => Some(impl_op!($x %= $yy)), + PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), + _ => None, }; } } @@ -797,13 +798,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(not(feature = "unchecked"))] return match op { - Token::PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), - Token::MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), - Token::MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), - Token::DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), - Token::ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), - Token::PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), - _ => None, + PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), + MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), + MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), + DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), + ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), + PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), + _ => None, }; #[cfg(feature = "unchecked")] @@ -811,13 +812,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(feature = "unchecked")] return match op { - Token::PlusAssign => Some(impl_op!(from $x += $yy)), - Token::MinusAssign => Some(impl_op!(from $x -= $yy)), - Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)), - Token::DivideAssign => Some(impl_op!(from $x /= $yy)), - Token::ModuloAssign => Some(impl_op!(from $x %= $yy)), - Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), - _ => None, + PlusAssign => Some(impl_op!(from $x += $yy)), + MinusAssign => Some(impl_op!(from $x -= $yy)), + MultiplyAssign => Some(impl_op!(from $x *= $yy)), + DivideAssign => Some(impl_op!(from $x /= $yy)), + ModuloAssign => Some(impl_op!(from $x %= $yy)), + PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), + _ => None, }; } }; @@ -832,7 +833,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let mut buf = [0_u8; 4]; let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); let mut x = args[0].write_lock::().expect(BUILTIN); @@ -843,14 +844,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt Ok((*x += ch).into()) }), - Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), + MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; } // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let ch = { let s = &*args[1].read_lock::().expect(BUILTIN); @@ -883,7 +884,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::Array; return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { { let x = std::mem::take(args[1]); let array = &mut *args[0].write_lock::().expect(BUILTIN); @@ -908,8 +909,10 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { + use crate::packages::blob_basic::blob_functions::*; + return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let x = args[1].as_int().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); @@ -917,7 +920,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt _ctx.engine() .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; - Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) + Ok(push(blob, x).into()) }), _ => None, }; @@ -925,8 +928,10 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { + use crate::packages::blob_basic::blob_functions::*; + return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let x = args[1].as_char().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); @@ -934,7 +939,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt _ctx.engine() .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; - Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) + Ok(append_char(blob, x).into()) }), _ => None, }; @@ -942,8 +947,10 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { + use crate::packages::blob_basic::blob_functions::*; + return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let s = std::mem::take(args[1]).cast::(); if s.is_empty() { @@ -959,7 +966,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt 0, ))?; - Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) + Ok(append_str(blob, &s).into()) }), _ => None, }; From 6880d449009b6e2bdee10619457907e976a7c4a5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Oct 2022 08:31:13 +0800 Subject: [PATCH 057/116] Remove unnecessary data. --- src/eval/expr.rs | 2 +- src/eval/stmt.rs | 4 ++-- src/types/scope.rs | 33 ++++++++++++++++++++------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/eval/expr.rs b/src/eval/expr.rs index c8d17896..6480da35 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -189,7 +189,7 @@ impl Engine { let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match scope.get_index(var_name) { - Some((index, _)) => index, + Some(index) => index, None => { return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) { Some(val) => Ok((val.into(), var_pos)), diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 4e26d7d3..18f5465f 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -959,7 +959,7 @@ impl Engine { Stmt::Export(x, ..) => { let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x; // Mark scope variables as public - if let Some((index, ..)) = scope.get_index(name) { + if let Some(index) = scope.get_index(name) { let alias = if alias.is_empty() { name } else { alias }.clone(); scope.add_alias_by_index(index, alias.into()); Ok(Dynamic::UNIT) @@ -971,7 +971,7 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] Stmt::Share(name, pos) => { - if let Some((index, ..)) = scope.get_index(name) { + if let Some(index) = scope.get_index(name) { let val = scope.get_mut_by_index(index); if !val.is_shared() { diff --git a/src/types/scope.rs b/src/types/scope.rs index a3787421..05d566a8 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -407,7 +407,7 @@ impl Scope<'_> { /// Find an entry in the [`Scope`], starting from the last. #[inline] #[must_use] - pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> { + pub(crate) fn get_index(&self, name: &str) -> Option { let len = self.len(); self.names @@ -417,7 +417,7 @@ impl Scope<'_> { .find_map(|(i, key)| { if name == key { let index = len - 1 - i; - Some((index, self.values[index].access_mode())) + Some(index) } else { None } @@ -467,10 +467,11 @@ impl Scope<'_> { #[inline] #[must_use] pub fn is_constant(&self, name: &str) -> Option { - self.get_index(name).map(|(.., access)| match access { - AccessMode::ReadWrite => false, - AccessMode::ReadOnly => true, - }) + self.get_index(name) + .map(|n| match self.values[n].access_mode() { + AccessMode::ReadWrite => false, + AccessMode::ReadOnly => true, + }) } /// Update the value of the named entry in the [`Scope`] if it already exists and is not constant. /// Push a new entry with the value into the [`Scope`] if the name doesn't exist or if the @@ -503,7 +504,10 @@ impl Scope<'_> { name: impl AsRef + Into, value: impl Variant + Clone, ) -> &mut Self { - match self.get_index(name.as_ref()) { + match self + .get_index(name.as_ref()) + .map(|n| (n, self.values[n].access_mode())) + { None | Some((.., AccessMode::ReadOnly)) => { self.push(name, value); } @@ -542,7 +546,10 @@ impl Scope<'_> { name: impl AsRef + Into, value: impl Variant + Clone, ) -> &mut Self { - match self.get_index(name.as_ref()) { + match self + .get_index(name.as_ref()) + .map(|n| (n, self.values[n].access_mode())) + { None => { self.push(name, value); } @@ -576,7 +583,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn get(&self, name: &str) -> Option<&Dynamic> { - self.get_index(name).map(|(index, _)| &self.values[index]) + self.get_index(name).map(|index| &self.values[index]) } /// Remove the last entry in the [`Scope`] by the specified name and return its value. /// @@ -607,7 +614,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn remove(&mut self, name: &str) -> Option { - self.get_index(name).and_then(|(index, _)| { + self.get_index(name).and_then(|index| { self.names.remove(index); self.aliases.remove(index); self.values.remove(index).try_cast() @@ -640,8 +647,8 @@ impl Scope<'_> { #[must_use] pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { self.get_index(name) - .and_then(move |(index, access)| match access { - AccessMode::ReadWrite => Some(self.get_mut_by_index(index)), + .and_then(move |n| match self.values[n].access_mode() { + AccessMode::ReadWrite => Some(self.get_mut_by_index(n)), AccessMode::ReadOnly => None, }) } @@ -685,7 +692,7 @@ impl Scope<'_> { name: impl AsRef + Into, alias: impl Into, ) { - if let Some((index, ..)) = self.get_index(name.as_ref()) { + if let Some(index) = self.get_index(name.as_ref()) { let alias = match alias.into() { x if x.is_empty() => name.into(), x => x, From 6702fe349c117ffe2fa197f32265a1dbe9f3c0f8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Oct 2022 10:05:31 +0800 Subject: [PATCH 058/116] Add index to Share statement. --- src/ast/stmt.rs | 5 ++++- src/eval/expr.rs | 2 +- src/eval/stmt.rs | 11 ++++++++--- src/parser.rs | 5 ++++- src/types/scope.rs | 16 ++++++++-------- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 6b15061c..80ed1b36 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -614,7 +614,10 @@ pub enum Stmt { /// This variant does not map to any language structure. It is currently only used only to /// convert a normal variable into a shared variable when the variable is _captured_ by a closure. #[cfg(not(feature = "no_closure"))] - Share(crate::ImmutableString, Position), + Share( + Box<(crate::ImmutableString, Option)>, + Position, + ), } impl Default for Stmt { diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 6480da35..5afd07d9 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -188,7 +188,7 @@ impl Engine { // Find the variable in the scope let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); - match scope.get_index(var_name) { + match scope.search(var_name) { Some(index) => index, None => { return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 18f5465f..770d4174 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -959,7 +959,7 @@ impl Engine { Stmt::Export(x, ..) => { let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x; // Mark scope variables as public - if let Some(index) = scope.get_index(name) { + if let Some(index) = scope.search(name) { let alias = if alias.is_empty() { name } else { alias }.clone(); scope.add_alias_by_index(index, alias.into()); Ok(Dynamic::UNIT) @@ -970,8 +970,13 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] - Stmt::Share(name, pos) => { - if let Some(index) = scope.get_index(name) { + Stmt::Share(x, pos) => { + let (name, index) = &**x; + + if let Some(index) = index + .map(|n| scope.len() - n.get()) + .or_else(|| scope.search(name)) + { let val = scope.get_mut_by_index(index); if !val.is_shared() { diff --git a/src/parser.rs b/src/parser.rs index cb002908..e2bcb181 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3711,7 +3711,10 @@ impl Engine { statements.extend( externals .into_iter() - .map(|crate::ast::Ident { name, pos }| Stmt::Share(name, pos)), + .map(|crate::ast::Ident { name, pos }| { + let (index, _) = parent.access_var(&name, lib, pos); + Stmt::Share((name, index).into(), pos) + }), ); statements.push(Stmt::Expr(expr.into())); Expr::Stmt(crate::ast::StmtBlock::new(statements, pos, Position::NONE).into()) diff --git a/src/types/scope.rs b/src/types/scope.rs index 05d566a8..435042e8 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -407,7 +407,7 @@ impl Scope<'_> { /// Find an entry in the [`Scope`], starting from the last. #[inline] #[must_use] - pub(crate) fn get_index(&self, name: &str) -> Option { + pub(crate) fn search(&self, name: &str) -> Option { let len = self.len(); self.names @@ -467,7 +467,7 @@ impl Scope<'_> { #[inline] #[must_use] pub fn is_constant(&self, name: &str) -> Option { - self.get_index(name) + self.search(name) .map(|n| match self.values[n].access_mode() { AccessMode::ReadWrite => false, AccessMode::ReadOnly => true, @@ -505,7 +505,7 @@ impl Scope<'_> { value: impl Variant + Clone, ) -> &mut Self { match self - .get_index(name.as_ref()) + .search(name.as_ref()) .map(|n| (n, self.values[n].access_mode())) { None | Some((.., AccessMode::ReadOnly)) => { @@ -547,7 +547,7 @@ impl Scope<'_> { value: impl Variant + Clone, ) -> &mut Self { match self - .get_index(name.as_ref()) + .search(name.as_ref()) .map(|n| (n, self.values[n].access_mode())) { None => { @@ -583,7 +583,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn get(&self, name: &str) -> Option<&Dynamic> { - self.get_index(name).map(|index| &self.values[index]) + self.search(name).map(|index| &self.values[index]) } /// Remove the last entry in the [`Scope`] by the specified name and return its value. /// @@ -614,7 +614,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn remove(&mut self, name: &str) -> Option { - self.get_index(name).and_then(|index| { + self.search(name).and_then(|index| { self.names.remove(index); self.aliases.remove(index); self.values.remove(index).try_cast() @@ -646,7 +646,7 @@ impl Scope<'_> { #[inline] #[must_use] pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { - self.get_index(name) + self.search(name) .and_then(move |n| match self.values[n].access_mode() { AccessMode::ReadWrite => Some(self.get_mut_by_index(n)), AccessMode::ReadOnly => None, @@ -692,7 +692,7 @@ impl Scope<'_> { name: impl AsRef + Into, alias: impl Into, ) { - if let Some(index) = self.get_index(name.as_ref()) { + if let Some(index) = self.search(name.as_ref()) { let alias = match alias.into() { x if x.is_empty() => name.into(), x => x, From 6b24cc151e455a89754feca155cc3b22a9fb17a8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Oct 2022 20:53:27 +0800 Subject: [PATCH 059/116] Add auto-gen notice to build template. --- build.rs | 1 + build.template | 2 ++ src/config.rs | 2 ++ 3 files changed, 5 insertions(+) diff --git a/build.rs b/build.rs index fa7570c5..66903154 100644 --- a/build.rs +++ b/build.rs @@ -6,6 +6,7 @@ use std::{ fn main() { // Tell Cargo that if the given environment variable changes, to rerun this build script. + println!("cargo:rerun-if-changed=build.template"); println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); let mut contents = String::new(); diff --git a/build.template b/build.template index b55d13e8..6f09909f 100644 --- a/build.template +++ b/build.template @@ -1,4 +1,6 @@ //! Configuration settings for this Rhai build +//! +//! This file is auto-generated from `build.template` /// Fixed hashing seeds for stable hashing. /// Set to [`None`] to disable stable hashing. diff --git a/src/config.rs b/src/config.rs index 9eb456fd..93db79d5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,6 @@ //! Configuration settings for this Rhai build +//! +//! This file is auto-generated from `build.template` /// Fixed hashing seeds for stable hashing. /// Set to [`None`] to disable stable hashing. From 3c2e0318836fe44b9faeb1a386bb6a2794dcc39d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 13:38:21 +0800 Subject: [PATCH 060/116] Use variable interpolation for println!. --- examples/arrays_and_structs.rs | 6 +-- examples/callback.rs | 2 +- examples/custom_types.rs | 4 +- examples/custom_types_and_methods.rs | 4 +- examples/event_handler_js/main.rs | 8 ++-- examples/event_handler_main/main.rs | 6 +-- examples/event_handler_map/main.rs | 8 ++-- examples/hello.rs | 2 +- examples/reuse_scope.rs | 2 +- examples/serde.rs | 8 ++-- examples/simple_fn.rs | 2 +- examples/strings.rs | 4 +- examples/threading.rs | 2 +- src/ast/expr.rs | 16 +++---- src/ast/namespace.rs | 2 +- src/ast/stmt.rs | 4 +- src/bin/rhai-dbg.rs | 65 ++++++++++++++-------------- src/bin/rhai-repl.rs | 41 +++++++++--------- src/bin/rhai-run.rs | 11 +++-- src/engine.rs | 20 +++------ src/eval/debugger.rs | 33 +++++--------- src/func/callable_function.rs | 16 +++---- src/types/dynamic.rs | 4 +- src/types/parse_error.rs | 12 ++--- 24 files changed, 132 insertions(+), 150 deletions(-) diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs index fc937bcd..b5a7889a 100644 --- a/examples/arrays_and_structs.rs +++ b/examples/arrays_and_structs.rs @@ -38,7 +38,7 @@ fn main() -> Result<(), Box> { engine .gen_fn_signatures(false) .into_iter() - .for_each(|func| println!("{}", func)); + .for_each(|func| println!("{func}")); println!(); } @@ -51,7 +51,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("{:?}", result); + println!("{result:?}"); let result = engine.eval::( " @@ -61,7 +61,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("{:?}", result); + println!("{result:?}"); Ok(()) } diff --git a/examples/callback.rs b/examples/callback.rs index ce27f77c..99984691 100644 --- a/examples/callback.rs +++ b/examples/callback.rs @@ -36,7 +36,7 @@ fn main() -> Result<(), Box> { let r2 = func(1, 2); let r3 = func(1, 2); - println!("The Answers: {}, {}, {}", r1, r2, r3); // prints 40, 42, 44 + println!("The Answers: {r1}, {r2}, {r3}"); // prints 40, 42, 44 Ok(()) } diff --git a/examples/custom_types.rs b/examples/custom_types.rs index ba57de58..c4d7804f 100644 --- a/examples/custom_types.rs +++ b/examples/custom_types.rs @@ -67,7 +67,7 @@ fn main() -> Result<(), Box> { engine .gen_fn_signatures(false) .into_iter() - .for_each(|func| println!("{}", func)); + .for_each(|func| println!("{func}")); println!(); } @@ -89,7 +89,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("result: {}", result); // prints 1085764 + println!("result: {result}"); // prints 1085764 Ok(()) } diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index a70265ff..fecbccfe 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -48,7 +48,7 @@ fn main() -> Result<(), Box> { engine .gen_fn_signatures(false) .into_iter() - .for_each(|func| println!("{}", func)); + .for_each(|func| println!("{func}")); println!(); } @@ -62,7 +62,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("result: {}", result); // prints 1085764 + println!("result: {result}"); // prints 1085764 Ok(()) } diff --git a/examples/event_handler_js/main.rs b/examples/event_handler_js/main.rs index db5740f3..22c128c3 100644 --- a/examples/event_handler_js/main.rs +++ b/examples/event_handler_js/main.rs @@ -78,12 +78,12 @@ pub fn main() { scope.push_constant("MY_CONSTANT", 42_i64); // Compile the handler script. - println!("> Loading script file: {}", path); + println!("> Loading script file: {path}"); let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { Ok(ast) => ast, Err(err) => { - eprintln!("! Error: {}", err); + eprintln!("! Error: {err}"); println!("Cannot continue. Bye!"); return; } @@ -101,7 +101,7 @@ pub fn main() { let result = engine.call_fn_raw(&mut scope, &ast, false, true, "init", Some(&mut states), []); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } // Create handler instance @@ -152,7 +152,7 @@ pub fn main() { engine.call_fn_raw(scope, ast, false, true, event, this_ptr, [arg.into()]); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } } } diff --git a/examples/event_handler_main/main.rs b/examples/event_handler_main/main.rs index c70c8740..b5b46577 100644 --- a/examples/event_handler_main/main.rs +++ b/examples/event_handler_main/main.rs @@ -67,7 +67,7 @@ pub fn main() { scope.push_constant("MY_CONSTANT", 42_i64); // Compile the handler script. - println!("> Loading script file: {}", path); + println!("> Loading script file: {path}"); let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { Ok(ast) => ast, @@ -89,7 +89,7 @@ pub fn main() { let result = engine.call_fn_raw(&mut scope, &ast, false, false, "init", None, []); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } // Create handler instance @@ -127,7 +127,7 @@ pub fn main() { let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),)); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } } } diff --git a/examples/event_handler_map/main.rs b/examples/event_handler_map/main.rs index e45688db..b41795c4 100644 --- a/examples/event_handler_map/main.rs +++ b/examples/event_handler_map/main.rs @@ -81,12 +81,12 @@ pub fn main() { scope.push("state", states); // Compile the handler script. - println!("> Loading script file: {}", path); + println!("> Loading script file: {path}"); let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { Ok(ast) => ast, Err(err) => { - eprintln!("! Error: {}", err); + eprintln!("! Error: {err}"); println!("Cannot continue. Bye!"); return; } @@ -103,7 +103,7 @@ pub fn main() { let result = engine.call_fn::<()>(&mut scope, &ast, "init", ()); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } // Create handler instance @@ -141,7 +141,7 @@ pub fn main() { let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),)); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } } } diff --git a/examples/hello.rs b/examples/hello.rs index b85b06da..dba66e5b 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Box> { let result = engine.eval::("40 + 2")?; - println!("The Answer: {}", result); // prints 42 + println!("The Answer: {result}"); // prints 42 Ok(()) } diff --git a/examples/reuse_scope.rs b/examples/reuse_scope.rs index 3bfa6bf3..486382a2 100644 --- a/examples/reuse_scope.rs +++ b/examples/reuse_scope.rs @@ -13,7 +13,7 @@ fn main() -> Result<(), Box> { for _ in 0..10 { let result = engine.eval_with_scope::(&mut scope, "x += 1; x")?; - println!("result: {}", result); + println!("result: {result}"); } println!("x = {}", scope.get_value::("x").unwrap()); diff --git a/examples/serde.rs b/examples/serde.rs index b145e247..cbdab2b2 100644 --- a/examples/serde.rs +++ b/examples/serde.rs @@ -36,13 +36,13 @@ fn main() { }, }; - println!("Source struct: {:#?}", x); + println!("Source struct: {x:#?}"); // Convert the 'MyStruct' into a 'Dynamic' let map: Dynamic = to_dynamic(x).unwrap(); assert!(map.is::()); - println!("Serialized to Dynamic: {:#?}", map); + println!("Serialized to Dynamic: {map:#?}"); } pub fn de() { @@ -60,7 +60,7 @@ fn main() { ) .unwrap(); - println!("Source Dynamic: {:#?}", result); + println!("Source Dynamic: {result:#?}"); // Convert the 'Dynamic' object map into 'MyStruct' let x: MyStruct = from_dynamic(&result).unwrap(); @@ -77,7 +77,7 @@ fn main() { }, } ); - println!("Deserialized to struct: {:#?}", x); + println!("Deserialized to struct: {x:#?}"); } ser(); diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs index 06b28c53..887cad30 100644 --- a/examples/simple_fn.rs +++ b/examples/simple_fn.rs @@ -13,7 +13,7 @@ fn main() -> Result<(), Box> { let result = engine.eval::("add(40, 2)")?; - println!("Answer: {}", result); // prints 42 + println!("Answer: {result}"); // prints 42 Ok(()) } diff --git a/examples/strings.rs b/examples/strings.rs index e3fac382..b9c7285a 100644 --- a/examples/strings.rs +++ b/examples/strings.rs @@ -37,10 +37,10 @@ fn main() -> Result<(), Box> { .register_fn("index_of", find_substring) // Register string functions using closures .register_fn("display", |label: &str, value: i64| { - println!("{}: {}", label, value) + println!("{label}: {value}") }) .register_fn("display", |label: ImmutableString, value: &str| { - println!(r#"{}: "{}""#, label, value) // Quote the input string + println!(r#"{label}: "{value}""#) // Quote the input string }); let mut scope = Scope::new(); diff --git a/examples/threading.rs b/examples/threading.rs index 64cd04dc..adcf9bea 100644 --- a/examples/threading.rs +++ b/examples/threading.rs @@ -60,7 +60,7 @@ fn main() { let mut value: i64 = 0; while value < 10 { - println!("Value: {}", value); + println!("Value: {value}"); // Send value to script tx_master.send(value).unwrap(); // Receive value from script diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 72d866fc..7a9e1cae 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -465,13 +465,13 @@ impl fmt::Debug for Expr { let mut display_pos = format!(" @ {:?}", self.start_position()); match self { - Self::DynamicConstant(value, ..) => write!(f, "{:?}", value), - Self::BoolConstant(value, ..) => write!(f, "{:?}", value), - Self::IntegerConstant(value, ..) => write!(f, "{:?}", value), + Self::DynamicConstant(value, ..) => write!(f, "{value:?}"), + Self::BoolConstant(value, ..) => write!(f, "{value:?}"), + Self::IntegerConstant(value, ..) => write!(f, "{value:?}"), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(value, ..) => write!(f, "{:?}", value), - Self::CharConstant(value, ..) => write!(f, "{:?}", value), - Self::StringConstant(value, ..) => write!(f, "{:?}", value), + Self::FloatConstant(value, ..) => write!(f, "{value:?}"), + Self::CharConstant(value, ..) => write!(f, "{value:?}"), + Self::StringConstant(value, ..) => write!(f, "{value:?}"), Self::Unit(..) => f.write_str("()"), Self::InterpolatedString(x, ..) => { @@ -502,10 +502,10 @@ impl fmt::Debug for Expr { f.write_str(&x.3)?; #[cfg(not(feature = "no_module"))] if let Some(n) = x.1.index() { - write!(f, " #{}", n)?; + write!(f, " #{n}")?; } if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { - write!(f, " #{}", n)?; + write!(f, " #{n}")?; } f.write_str(")") } diff --git a/src/ast/namespace.rs b/src/ast/namespace.rs index e0939384..2888b25e 100644 --- a/src/ast/namespace.rs +++ b/src/ast/namespace.rs @@ -37,7 +37,7 @@ impl fmt::Debug for Namespace { } if let Some(index) = self.index { - write!(f, "{} -> ", index)?; + write!(f, "{index} -> ")?; } f.write_str( diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 80ed1b36..284d0133 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -185,8 +185,8 @@ impl fmt::Debug for RangeCase { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {}", r.start, r.end, n), - Self::InclusiveInt(r, n) => write!(f, "{}..={} => {}", *r.start(), *r.end(), n), + Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {n}", r.start, r.end), + Self::InclusiveInt(r, n) => write!(f, "{}..={} => {n}", *r.start(), *r.end()), } } } diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index 71d5152a..5649c0c1 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -75,7 +75,7 @@ fn print_current_source( } if !src.is_empty() { // Print just a line number for imported modules - println!("{} @ {:?}", src, pos); + println!("{src} @ {pos:?}"); } else { // Print the current source line print_source(lines, pos, 0, window); @@ -100,17 +100,16 @@ fn print_error(input: &str, mut err: EvalAltResult) { // Print error position if pos.is_none() { // No position - println!("{}", err); + println!("{err}"); } else { // Specific position - print line text - println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); + println!("{line_no}{}", lines[pos.line().unwrap() - 1]); // Display position marker println!( - "{0:>1$} {2}", + "{0:>1$} {err}", "^", line_no.len() + pos.position().unwrap(), - err ); } } @@ -247,11 +246,11 @@ fn debug_callback( BreakPoint::AtPosition { .. } => (), BreakPoint::AtFunctionName { ref name, .. } | BreakPoint::AtFunctionCall { ref name, .. } => { - println!("! Call to function {}.", name) + println!("! Call to function {name}.") } #[cfg(not(feature = "no_object"))] BreakPoint::AtProperty { ref name, .. } => { - println!("! Property {} accessed.", name) + println!("! Property {name} accessed.") } _ => unreachable!(), } @@ -311,8 +310,8 @@ fn debug_callback( println!("{:?}", node); } else { match source { - Some(source) => println!("{:?} {} @ {:?}", node, source, pos), - None => println!("{:?} @ {:?}", node, pos), + Some(source) => println!("{node:?} {source} @ {pos:?}"), + None => println!("{node:?} @ {pos:?}"), } } println!(); @@ -327,7 +326,7 @@ fn debug_callback( ["list" | "l", n] if n.parse::().is_ok() => { let num = n.parse::().unwrap(); if num == 0 || num > lines.len() { - eprintln!("\x1b[31mInvalid line: {}\x1b[39m", num); + eprintln!("\x1b[31mInvalid line: {num}\x1b[39m"); } else { let pos = Position::new(num as u16, 0); print_current_source(&mut context, source, pos, lines, (3, 6)); @@ -340,17 +339,17 @@ fn debug_callback( ["next" | "n"] => break Ok(DebuggerCommand::Next), ["scope"] => println!("{}", context.scope()), ["print" | "p", "this"] => match context.this_ptr() { - Some(value) => println!("=> {:?}", value), + Some(value) => println!("=> {value:?}"), None => println!("`this` pointer is unbound."), }, ["print" | "p", var_name] => match context.scope().get_value::(var_name) { - Some(value) => println!("=> {:?}", value), - None => eprintln!("Variable not found: {}", var_name), + Some(value) => println!("=> {value:?}"), + None => eprintln!("Variable not found: {var_name}"), }, ["print" | "p"] => { println!("{}", context.scope().clone_visible()); if let Some(value) = context.this_ptr() { - println!("this = {:?}", value); + println!("this = {value:?}"); } } #[cfg(not(feature = "no_module"))] @@ -379,7 +378,7 @@ fn debug_callback( .iter() .rev() { - println!("{}", frame) + println!("{frame}") } } ["info" | "i", "break" | "b"] => Iterator::for_each( @@ -396,7 +395,7 @@ fn debug_callback( print!("{}", line_num); print_source(lines, *pos, line_num.len(), (0, 0)); } - _ => println!("[{}] {}", i + 1, bp), + _ => println!("[{}] {bp}", i + 1), }, ), ["enable" | "en", n] => { @@ -414,12 +413,12 @@ fn debug_callback( .get_mut(n - 1) .unwrap() .enable(true); - println!("Break-point #{} enabled.", n) + println!("Break-point #{n} enabled.") } else { - eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m"); } } else { - eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m"); } } ["disable" | "dis", n] => { @@ -437,12 +436,12 @@ fn debug_callback( .get_mut(n - 1) .unwrap() .enable(false); - println!("Break-point #{} disabled.", n) + println!("Break-point #{n} disabled.") } else { - eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m"); } } else { - eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m"); } } ["delete" | "d", n] => { @@ -458,12 +457,12 @@ fn debug_callback( .debugger .break_points_mut() .remove(n - 1); - println!("Break-point #{} deleted.", n) + println!("Break-point #{n} deleted.") } else { - eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m"); } } else { - eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m"); } } ["delete" | "d"] => { @@ -481,14 +480,14 @@ fn debug_callback( args, enabled: true, }; - println!("Break-point added for {}", bp); + println!("Break-point added for {bp}"); context .global_runtime_state_mut() .debugger .break_points_mut() .push(bp); } else { - eprintln!("\x1b[31mInvalid number of arguments: '{}'\x1b[39m", args); + eprintln!("\x1b[31mInvalid number of arguments: '{args}'\x1b[39m"); } } // Property name @@ -498,7 +497,7 @@ fn debug_callback( name: param[1..].into(), enabled: true, }; - println!("Break-point added for {}", bp); + println!("Break-point added for {bp}"); context .global_runtime_state_mut() .debugger @@ -521,14 +520,14 @@ fn debug_callback( pos: Position::new(n as u16, 0), enabled: true, }; - println!("Break-point added {}", bp); + println!("Break-point added {bp}"); context .global_runtime_state_mut() .debugger .break_points_mut() .push(bp); } else { - eprintln!("\x1b[31mInvalid line number: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid line number: '{n}'\x1b[39m"); } } // Function name parameter @@ -537,7 +536,7 @@ fn debug_callback( name: param.trim().into(), enabled: true, }; - println!("Break-point added for {}", bp); + println!("Break-point added for {bp}"); context .global_runtime_state_mut() .debugger @@ -551,7 +550,7 @@ fn debug_callback( pos, enabled: true, }; - println!("Break-point added {}", bp); + println!("Break-point added {bp}"); context .global_runtime_state_mut() .debugger @@ -588,7 +587,7 @@ fn debug_callback( fn main() { let title = format!("Rhai Debugger (version {})", env!("CARGO_PKG_VERSION")); - println!("{}", title); + println!("{title}"); println!("{0:=<1$}", "", title.len()); // Initialize scripting engine diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index dcab9974..71801d82 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -26,17 +26,16 @@ fn print_error(input: &str, mut err: EvalAltResult) { // Print error position if pos.is_none() { // No position - println!("{}", err); + println!("{err}"); } else { // Specific position - print line text - println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); + println!("{line_no}{}", lines[pos.line().unwrap() - 1]); // Display position marker println!( - "{0:>1$} {2}", + "{0:>1$} {err}", "^", line_no.len() + pos.position().unwrap(), - err ); } } @@ -119,7 +118,7 @@ fn load_script_files(engine: &mut Engine) { for filename in env::args().skip(1) { let filename = match Path::new(&filename).canonicalize() { Err(err) => { - eprintln!("Error script file path: {}\n{}", filename, err); + eprintln!("Error script file path: {filename}\n{err}"); exit(1); } Ok(f) => { @@ -164,7 +163,7 @@ fn load_script_files(engine: &mut Engine) { let filename = filename.to_string_lossy(); eprintln!("{:=<1$}", "", filename.len()); - eprintln!("{}", filename); + eprintln!("{filename}"); eprintln!("{:=<1$}", "", filename.len()); eprintln!(); @@ -277,13 +276,13 @@ mod sample_functions { #[rhai_fn(name = "test")] pub fn test2(x: &mut INT, y: INT, z: &str) { *x += y + (z.len() as INT); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } } fn main() { let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION")); - println!("{}", title); + println!("{title}"); println!("{0:=<1$}", "", title.len()); #[cfg(not(feature = "no_optimize"))] @@ -338,11 +337,11 @@ fn main() { history_offset += 1; } if input.contains('\n') { - println!("[{}] ~~~~", replacement_index); - println!("{}", input); + println!("[{replacement_index}] ~~~~"); + println!("{input}"); println!("~~~~"); } else { - println!("[{}] {}", replacement_index, input); + println!("[{replacement_index}] {input}"); } replacement_index = 0; } else { @@ -374,7 +373,7 @@ fn main() { Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break 'main_loop, Err(err) => { - eprintln!("Error: {:?}", err); + eprintln!("Error: {err:?}"); break 'main_loop; } } @@ -401,12 +400,12 @@ fn main() { "history" => { for (i, h) in rl.history().iter().enumerate() { match &h.split('\n').collect::>()[..] { - [line] => println!("[{}] {}", history_offset + i, line), + [line] => println!("[{}] {line}", history_offset + i), lines => { for (x, line) in lines.iter().enumerate() { let number = format!("[{}]", history_offset + i); if x == 0 { - println!("{} {}", number, line.trim_end()); + println!("{number} {}", line.trim_end()); } else { println!("{0:>1$} {2}", "", number.len(), line.trim_end()); } @@ -439,30 +438,30 @@ fn main() { continue; } "scope" => { - println!("{}", scope); + println!("{scope}"); continue; } #[cfg(not(feature = "no_optimize"))] "astu" => { // print the last un-optimized AST - println!("{:#?}\n", ast_u); + println!("{ast_u:#?}\n"); continue; } "ast" => { // print the last AST - println!("{:#?}\n", ast); + println!("{ast:#?}\n"); continue; } #[cfg(feature = "metadata")] "functions" => { // print a list of all registered functions for f in engine.gen_fn_signatures(false) { - println!("{}", f) + println!("{f}") } #[cfg(not(feature = "no_function"))] for f in main_ast.iter_functions() { - println!("{}", f) + println!("{f}") } println!(); @@ -505,7 +504,7 @@ fn main() { replacement = Some(line.clone()); replacement_index = history_offset + (rl.history().len() - 1 - n); } - None => eprintln!("History line not found: {}", text), + None => eprintln!("History line not found: {text}"), } continue; } @@ -561,7 +560,7 @@ fn main() { engine.eval_ast_with_scope::(&mut scope, &main_ast) }) { Ok(result) if !result.is::<()>() => { - println!("=> {:?}", result); + println!("=> {result:?}"); println!(); } Ok(_) => (), diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index e87e99b4..5bd464a3 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -7,12 +7,11 @@ fn eprint_error(input: &str, mut err: EvalAltResult) { let line = pos.line().unwrap(); let line_no = format!("{line}: "); - eprintln!("{}{}", line_no, lines[line - 1]); + eprintln!("{line_no}{}", lines[line - 1]); eprintln!( - "{:>1$} {2}", + "{:>1$} {err_msg}", "^", line_no.len() + pos.position().unwrap(), - err_msg ); eprintln!(); } @@ -24,7 +23,7 @@ fn eprint_error(input: &str, mut err: EvalAltResult) { if pos.is_none() { // No position - eprintln!("{}", err); + eprintln!("{err}"); } else { // Specific position eprint_line(&lines, pos, &err.to_string()) @@ -37,7 +36,7 @@ fn main() { for filename in env::args().skip(1) { let filename = match Path::new(&filename).canonicalize() { Err(err) => { - eprintln!("Error script file path: {}\n{}", filename, err); + eprintln!("Error script file path: {filename}\n{err}"); exit(1); } Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) @@ -94,7 +93,7 @@ fn main() { let filename = filename.to_string_lossy(); eprintln!("{:=<1$}", "", filename.len()); - eprintln!("{}", filename); + eprintln!("{filename}"); eprintln!("{:=<1$}", "", filename.len()); eprintln!(); diff --git a/src/engine.rs b/src/engine.rs index 8edc62ff..8071b9de 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -85,7 +85,7 @@ pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax(); /// /// let result = engine.eval::("40 + 2")?; /// -/// println!("Answer: {}", result); // prints 42 +/// println!("Answer: {result}"); // prints 42 /// # Ok(()) /// # } /// ``` @@ -236,18 +236,12 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] { - engine.print = Box::new(|s| println!("{}", s)); - engine.debug = Box::new(|s, source, pos| { - source.map_or_else( - || { - if pos.is_none() { - println!("{s}"); - } else { - println!("{pos:?} | {s}"); - } - }, - |source| println!("{source} @ {pos:?} | {s}"), - ) + engine.print = Box::new(|s| println!("{s}")); + engine.debug = Box::new(|s, source, pos| match (source, pos) { + (Some(source), crate::Position::NONE) => println!("{source} | {s}"), + (Some(source), pos) => println!("{source} @ {pos:?} | {s}"), + (None, crate::Position::NONE) => println!("{s}"), + (None, pos) => println!("{pos:?} | {s}"), }); } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 249b0319..63bb3d22 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -148,35 +148,30 @@ impl fmt::Display for BreakPoint { pos, enabled, } => { - if source.is_empty() { - write!(f, "@ {:?}", pos)?; - } else { - write!(f, "{} @ {:?}", source, pos)?; + if !source.is_empty() { + write!(f, "{source} ")?; } + write!(f, "@ {pos:?}")?; if !*enabled { f.write_str(" (disabled)")?; } Ok(()) } - Self::AtFunctionName { - name: fn_name, - enabled, - } => { - write!(f, "{} (...)", fn_name)?; + Self::AtFunctionName { name, enabled } => { + write!(f, "{name} (...)")?; if !*enabled { f.write_str(" (disabled)")?; } Ok(()) } Self::AtFunctionCall { - name: fn_name, + name, args, enabled, } => { write!( f, - "{} ({})", - fn_name, + "{name} ({})", repeat("_").take(*args).collect::>().join(", ") )?; if !*enabled { @@ -185,11 +180,8 @@ impl fmt::Display for BreakPoint { Ok(()) } #[cfg(not(feature = "no_object"))] - Self::AtProperty { - name: prop, - enabled, - } => { - write!(f, ".{}", prop)?; + Self::AtProperty { name, enabled } => { + write!(f, ".{name}")?; if !*enabled { f.write_str(" (disabled)")?; } @@ -251,11 +243,10 @@ impl fmt::Display for CallStackFrame { fp.finish()?; if !self.pos.is_none() { - if self.source.is_empty() { - write!(f, " @ {:?}", self.pos)?; - } else { - write!(f, ": {} @ {:?}", self.source, self.pos)?; + if !self.source.is_empty() { + write!(f, ": {}", self.source)?; } + write!(f, " @ {:?}", self.pos)?; } Ok(()) diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index e6b85a29..f11d41a7 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -31,10 +31,10 @@ impl fmt::Debug for CallableFunction { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Pure(..) => write!(f, "NativePureFunction"), - Self::Method(..) => write!(f, "NativeMethod"), - Self::Iterator(..) => write!(f, "NativeIterator"), - Self::Plugin(..) => write!(f, "PluginFunction"), + Self::Pure(..) => f.write_str("NativePureFunction"), + Self::Method(..) => f.write_str("NativeMethod"), + Self::Iterator(..) => f.write_str("NativeIterator"), + Self::Plugin(..) => f.write_str("PluginFunction"), #[cfg(not(feature = "no_function"))] Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), @@ -45,10 +45,10 @@ impl fmt::Debug for CallableFunction { impl fmt::Display for CallableFunction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Pure(..) => write!(f, "NativePureFunction"), - Self::Method(..) => write!(f, "NativeMethod"), - Self::Iterator(..) => write!(f, "NativeIterator"), - Self::Plugin(..) => write!(f, "PluginFunction"), + Self::Pure(..) => f.write_str("NativePureFunction"), + Self::Method(..) => f.write_str("NativeMethod"), + Self::Iterator(..) => f.write_str("NativeIterator"), + Self::Plugin(..) => f.write_str("PluginFunction"), #[cfg(not(feature = "no_function"))] Self::Script(s) => fmt::Display::fmt(s, f), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 0c9b7661..c53b8ea3 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -416,7 +416,7 @@ impl Hash for Dynamic { impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { - Union::Unit(..) => write!(f, ""), + Union::Unit(..) => Ok(()), Union::Bool(ref v, ..) => fmt::Display::fmt(v, f), Union::Str(ref v, ..) => fmt::Display::fmt(v, f), Union::Char(ref v, ..) => fmt::Display::fmt(v, f), @@ -527,7 +527,7 @@ impl fmt::Debug for Dynamic { if i > 0 && i % 8 == 0 { f.write_str(" ")?; } - write!(f, "{:02x}", v) + write!(f, "{v:02x}") })?; f.write_str("]") } diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index 7ac362af..d11a49c6 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -38,11 +38,11 @@ impl Error for LexError {} impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), - Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s), - Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s), - Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s), - Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s), + Self::UnexpectedInput(s) => write!(f, "Unexpected '{s}'"), + Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{s}'"), + Self::MalformedNumber(s) => write!(f, "Invalid number: '{s}'"), + Self::MalformedChar(s) => write!(f, "Invalid character: '{s}'"), + Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{s}'"), Self::UnterminatedString => f.write_str("Open string is not terminated"), Self::StringTooLong(max) => write!( f, @@ -50,7 +50,7 @@ impl fmt::Display for LexError { max ), Self::ImproperSymbol(s, d) if d.is_empty() => { - write!(f, "Invalid symbol encountered: '{}'", s) + write!(f, "Invalid symbol encountered: '{s}'") } Self::ImproperSymbol(.., d) => f.write_str(d), } From 4708fde8bd6fd739dfa2a440db6cfd6793946bc0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 15:52:24 +0800 Subject: [PATCH 061/116] Compact data structures. --- src/eval/global_state.rs | 81 ++++++++++++++++++++++++---------------- src/func/call.rs | 4 +- src/func/native.rs | 2 +- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 98e39870..a22a45a2 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -23,9 +23,12 @@ pub type GlobalConstants = // corresponds to that key. #[derive(Clone)] pub struct GlobalRuntimeState<'a> { + /// Names of imported [modules][crate::Module]. + #[cfg(not(feature = "no_module"))] + imports: crate::StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec<(crate::ImmutableString, crate::Shared)>, + modules: crate::StaticVec>, /// Source of the current context. /// /// No source if the string is empty. @@ -77,6 +80,8 @@ impl GlobalRuntimeState<'_> { #[must_use] pub fn new(engine: &Engine) -> Self { Self { + #[cfg(not(feature = "no_module"))] + imports: crate::StaticVec::new_const(), #[cfg(not(feature = "no_module"))] modules: crate::StaticVec::new_const(), source: Identifier::new_const(), @@ -127,7 +132,7 @@ impl GlobalRuntimeState<'_> { #[inline(always)] #[must_use] pub fn get_shared_import(&self, index: usize) -> Option> { - self.modules.get(index).map(|(_, m)| m).cloned() + self.modules.get(index).cloned() } /// Get a mutable reference to the globally-imported [module][crate::Module] at a /// particular index. @@ -141,7 +146,7 @@ impl GlobalRuntimeState<'_> { &mut self, index: usize, ) -> Option<&mut crate::Shared> { - self.modules.get_mut(index).map(|(_, m)| m) + self.modules.get_mut(index) } /// Get the index of a globally-imported [module][crate::Module] by name. /// @@ -150,13 +155,11 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn find_import(&self, name: &str) -> Option { - let len = self.modules.len(); - - self.modules + self.imports .iter() .rev() - .position(|(key, _)| key.as_str() == name) - .map(|i| len - 1 - i) + .position(|key| key.as_str() == name) + .map(|i| self.imports.len() - 1 - i) } /// Push an imported [module][crate::Module] onto the stack. /// @@ -168,7 +171,8 @@ impl GlobalRuntimeState<'_> { name: impl Into, module: impl Into>, ) { - self.modules.push((name.into(), module.into())); + self.imports.push(name.into()); + self.modules.push(module.into()); } /// Truncate the stack of globally-imported [modules][crate::Module] to a particular length. /// @@ -176,17 +180,18 @@ impl GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn truncate_imports(&mut self, size: usize) { + self.imports.truncate(size); self.modules.truncate(size); } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - #[allow(dead_code)] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.modules + self.imports .iter() + .zip(self.modules.iter()) .rev() .map(|(name, module)| (name.as_str(), &**module)) } @@ -194,23 +199,32 @@ impl GlobalRuntimeState<'_> { /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - #[allow(dead_code)] #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { - self.modules.iter().rev() + ) -> 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. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - #[allow(dead_code)] #[inline] pub fn scan_imports_raw( &self, - ) -> impl Iterator)> { - self.modules.iter() + ) -> impl Iterator)> { + self.imports.iter().zip(self.modules.iter()) + } + /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of + /// globally-imported [modules][crate::Module]? + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + #[inline(always)] + pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool { + self.modules + .iter() + .any(|m| m.may_contain_dynamic_fn(hash_script)) } /// Does the specified function hash key exist in the stack of globally-imported /// [modules][crate::Module]? @@ -221,9 +235,7 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn contains_qualified_fn(&self, hash: u64) -> bool { - self.modules - .iter() - .any(|(_, m)| m.contains_qualified_fn(hash)) + self.modules.iter().any(|m| m.contains_qualified_fn(hash)) } /// Get the specified function via its hash key from the stack of globally-imported /// [modules][crate::Module]. @@ -239,7 +251,7 @@ impl GlobalRuntimeState<'_> { 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()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of /// globally-imported [modules][crate::Module]? @@ -250,9 +262,7 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn contains_iter(&self, id: std::any::TypeId) -> bool { - self.modules - .iter() - .any(|(_, m)| m.contains_qualified_iter(id)) + self.modules.iter().any(|m| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported /// [modules][crate::Module]. @@ -265,7 +275,7 @@ impl GlobalRuntimeState<'_> { self.modules .iter() .rev() - .find_map(|(_, m)| m.get_qualified_iter(id)) + .find_map(|m| m.get_qualified_iter(id)) } /// Get the current source. #[inline] @@ -309,25 +319,29 @@ impl GlobalRuntimeState<'_> { impl IntoIterator for GlobalRuntimeState<'_> { type Item = (crate::ImmutableString, crate::Shared); type IntoIter = std::iter::Rev< - smallvec::IntoIter< - [(crate::ImmutableString, crate::Shared); crate::STATIC_VEC_INLINE_SIZE], + std::iter::Zip< + smallvec::IntoIter<[crate::ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, + smallvec::IntoIter<[crate::Shared; crate::STATIC_VEC_INLINE_SIZE]>, >, >; fn into_iter(self) -> Self::IntoIter { - self.modules.into_iter().rev() + self.imports.into_iter().zip(self.modules.into_iter()).rev() } } #[cfg(not(feature = "no_module"))] impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { - type Item = &'a (crate::ImmutableString, crate::Shared); + type Item = (&'a crate::ImmutableString, &'a crate::Shared); type IntoIter = std::iter::Rev< - std::slice::Iter<'a, (crate::ImmutableString, crate::Shared)>, + std::iter::Zip< + std::slice::Iter<'a, crate::ImmutableString>, + std::slice::Iter<'a, crate::Shared>, + >, >; fn into_iter(self) -> Self::IntoIter { - self.modules.iter().rev() + self.imports.iter().zip(self.modules.iter()).rev() } } @@ -338,7 +352,8 @@ impl, M: Into>> Ext #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { - self.modules.push((k.into(), m.into())); + self.imports.push(k.into()); + self.modules.push(m.into()); } } } @@ -350,7 +365,7 @@ impl fmt::Debug for GlobalRuntimeState<'_> { let mut f = f.debug_struct("GlobalRuntimeState"); #[cfg(not(feature = "no_module"))] - f.field("imports", &self.modules); + f.field("imports", &self.scan_imports_raw().collect::>()); f.field("source", &self.source) .field("num_operations", &self.num_operations); diff --git a/src/func/call.rs b/src/func/call.rs index c3ca7511..9e0d6c31 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -258,9 +258,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] let is_dynamic = is_dynamic - || _global - .iter_imports_raw() - .any(|(_, m)| m.may_contain_dynamic_fn(hash_base)) + || _global.may_contain_dynamic_fn(hash_base) || self .global_sub_modules .values() diff --git a/src/func/native.rs b/src/func/native.rs index 707f8f4c..6afda4da 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -229,7 +229,7 @@ impl<'a> NativeCallContext<'a> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator)> { self.global.iter().flat_map(|&g| g.iter_imports_raw()) } /// _(internals)_ The current [`GlobalRuntimeState`], if any. From 41006455124eeece0e28c03676c34d4a6baa127d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 20:42:10 +0800 Subject: [PATCH 062/116] Move pure checking out of functions. --- CHANGELOG.md | 1 + Cargo.toml | 2 +- codegen/src/function.rs | 14 ++------ codegen/src/test/function.rs | 11 +++++-- codegen/src/test/module.rs | 64 ++++++++++++++---------------------- src/bin/rhai-repl.rs | 8 ++++- src/eval/target.rs | 11 ------- src/func/call.rs | 24 +++++++------- src/func/plugin.rs | 10 ++++++ src/func/register.rs | 45 ++++++++++++------------- src/types/error.rs | 32 ++++++++++++++++-- 11 files changed, 120 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d09c7d..49aa5966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Enhancements ------------ * `parse_json` function is added to parse a JSON string into an object map. +* `Error::ErrorNonPureMethodCallOnConstant` is added which is raised when a non-pure method is called on a constant value. Version 1.10.1 diff --git a/Cargo.toml b/Cargo.toml index d52f8b30..3999b7de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "codegen"] [package] name = "rhai" -version = "1.10.1" +version = "1.11.0" rust-version = "1.61.0" edition = "2018" resolver = "2" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index d356645e..833e3d98 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -673,6 +673,7 @@ impl ExportedFn { let sig_name = self.name().clone(); let arg_count = self.arg_count(); let is_method_call = self.mutable_receiver(); + let is_pure = !self.mutable_receiver() || self.params().pure.is_some(); let mut unpack_statements = Vec::new(); let mut unpack_exprs = Vec::new(); @@ -713,18 +714,6 @@ impl ExportedFn { }) .unwrap(), ); - if self.params().pure.is_none() { - let arg_lit_str = - syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span()); - unpack_statements.push( - syn::parse2::(quote! { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant(#arg_lit_str.to_string(), Position::NONE).into()); - } - }) - .unwrap(), - ); - } #[cfg(feature = "metadata")] input_type_names.push(arg_name); input_type_exprs.push( @@ -877,6 +866,7 @@ impl ExportedFn { } #[inline(always)] fn is_method_call(&self) -> bool { #is_method_call } + #[inline(always)] fn is_pure(&self) -> bool { #is_pure } } } } diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 8465cd61..05d99130 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -285,6 +285,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -323,6 +324,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -361,6 +363,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -401,6 +404,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -434,6 +438,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } }; @@ -467,6 +472,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -500,15 +506,13 @@ mod generate_tests { impl PluginFunction for Token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } #[allow(unused)] #[doc(hidden)] @@ -548,6 +552,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 881746b9..0a279452 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -390,6 +390,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -467,6 +468,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -525,6 +527,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -582,6 +585,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -653,6 +657,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(non_camel_case_types)] @@ -672,6 +677,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -730,6 +736,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -795,6 +802,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -864,14 +872,12 @@ mod generate_tests { impl PluginFunction for get_mystic_number_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_mystic_number(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1080,6 +1086,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1170,6 +1177,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1227,6 +1235,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1286,6 +1295,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1338,14 +1348,12 @@ mod generate_tests { impl PluginFunction for increment_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1401,14 +1409,12 @@ mod generate_tests { impl PluginFunction for increment_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } #[allow(unused_imports)] @@ -1487,14 +1493,12 @@ mod generate_tests { impl PluginFunction for increment_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } #[allow(unused_imports)] @@ -1574,14 +1578,12 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1638,14 +1640,12 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1699,15 +1699,13 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1764,15 +1762,13 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1826,15 +1822,13 @@ mod generate_tests { impl PluginFunction for get_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1896,15 +1890,13 @@ mod generate_tests { impl PluginFunction for get_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1961,15 +1953,13 @@ mod generate_tests { impl PluginFunction for get_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -2023,9 +2013,6 @@ mod generate_tests { impl PluginFunction for set_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg2 = mem::take(args[2usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); @@ -2033,6 +2020,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -2089,9 +2077,6 @@ mod generate_tests { impl PluginFunction for set_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg2 = mem::take(args[2usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); @@ -2099,6 +2084,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 71801d82..ea452269 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -309,7 +309,13 @@ fn main() { } // Register sample functions - engine.register_global_module(exported_module!(sample_functions).into()); + engine + .register_global_module(exported_module!(sample_functions).into()) + .register_get_set( + "test", + |x: &mut INT| *x % 2 == 0, + |x: &mut INT, y: bool| if y { *x *= 2 } else { *x /= 2 }, + ); // Create scope let mut scope = Scope::new(); diff --git a/src/eval/target.rs b/src/eval/target.rs index c48aa62a..a12aa96c 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -239,17 +239,6 @@ impl<'a> Target<'a> { _ => None, } } - /// Convert a shared or reference [`Target`] into a target with an owned value. - #[inline(always)] - #[must_use] - pub fn into_owned(self) -> Self { - match self { - Self::RefMut(r) => Self::TempValue(r.clone()), - #[cfg(not(feature = "no_closure"))] - Self::SharedValue { value, .. } => Self::TempValue(value), - _ => self, - } - } /// Get the source [`Dynamic`] of the [`Target`]. #[allow(dead_code)] #[inline] diff --git a/src/func/call.rs b/src/func/call.rs index 9e0d6c31..758767fb 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -418,7 +418,12 @@ impl Engine { let context = (self, name, source, &*global, lib, pos, level).into(); let result = if func.is_plugin_fn() { - func.get_plugin_fn().unwrap().call(context, args) + let f = func.get_plugin_fn().unwrap(); + if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { + Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) + } else { + f.call(context, args) + } } else { func.get_native_fn().unwrap()(context, args) }; @@ -1229,13 +1234,9 @@ impl Engine { .map(|(value, ..)| arg_values.push(value.flatten())) })?; - let (mut target, _pos) = + let (target, _pos) = self.search_namespace(scope, global, lib, this_ptr, first_expr, level)?; - if target.is_read_only() { - target = target.into_owned(); - } - self.track_operation(global, _pos)?; #[cfg(not(feature = "no_closure"))] @@ -1425,11 +1426,12 @@ impl Engine { Some(f) if f.is_plugin_fn() => { let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); - let result = f - .get_plugin_fn() - .expect("plugin function") - .clone() - .call(context, &mut args); + let f = f.get_plugin_fn().expect("plugin function"); + let result = if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { + Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into()) + } else { + f.call(context, &mut args) + }; self.check_return_value(result, pos) } diff --git a/src/func/plugin.rs b/src/func/plugin.rs index 7c4f84fe..b4a9b9d0 100644 --- a/src/func/plugin.rs +++ b/src/func/plugin.rs @@ -29,4 +29,14 @@ pub trait PluginFunction { /// Is this plugin function a method? #[must_use] fn is_method_call(&self) -> bool; + + /// Is this plugin function pure? + /// + /// This defaults to `true` such that any old implementation that has constant-checking code + /// inside the function itself will continue to work. + #[inline(always)] + #[must_use] + fn is_pure(&self) -> bool { + true + } } diff --git a/src/func/register.rs b/src/func/register.rs index 928b08b0..8899548b 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -96,26 +96,27 @@ pub trait RegisterNativeFunction { const EXPECT_ARGS: &str = "arguments"; macro_rules! check_constant { - ($ctx:ident, $args:ident) => { + ($abi:ident, $ctx:ident, $args:ident) => { #[cfg(any(not(feature = "no_object"), not(feature = "no_index")))] - { - let args_len = $args.len(); - - if args_len > 0 && $args[0].is_read_only() { - let deny = match $ctx.fn_name() { - #[cfg(not(feature = "no_object"))] - f if args_len == 2 && f.starts_with(crate::engine::FN_SET) => true, - #[cfg(not(feature = "no_index"))] - crate::engine::FN_IDX_SET if args_len == 3 => true, - _ => false, - }; - if deny { - return Err(crate::ERR::ErrorAssignmentToConstant( - String::new(), - crate::Position::NONE, - ) - .into()); + if stringify!($abi) == "Method" && !$args.is_empty() { + let deny = match $args.len() { + #[cfg(not(feature = "no_index"))] + 3 if $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only() => true, + #[cfg(not(feature = "no_object"))] + 2 if $ctx.fn_name().starts_with(crate::engine::FN_SET) + && $args[0].is_read_only() => + { + true } + _ => false, + }; + + if deny { + return Err(crate::ERR::ErrorNonPureMethodCallOnConstant( + $ctx.fn_name().to_string(), + crate::Position::NONE, + ) + .into()); } } }; @@ -144,7 +145,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(_ctx, args); + check_constant!($abi, _ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* @@ -169,7 +170,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(ctx, args); + check_constant!($abi, ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* @@ -195,7 +196,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(_ctx, args); + check_constant!($abi, _ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* @@ -218,7 +219,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(ctx, args); + check_constant!($abi, ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* diff --git a/src/types/error.rs b/src/types/error.rs index 89bbedd8..e18414de 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,5 +1,6 @@ //! Module containing error definitions for the evaluation process. +use crate::engine::{FN_GET, FN_IDX_GET, FN_IDX_SET, FN_SET}; use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; #[cfg(feature = "no_std")] use core_error::Error; @@ -84,6 +85,8 @@ pub enum EvalAltResult { /// Data race detected when accessing a variable. Wrapped value is the variable name. ErrorDataRace(String, Position), + /// Calling a non-pure method on a constant. Wrapped value is the function name. + ErrorNonPureMethodCallOnConstant(String, Position), /// Assignment to a constant variable. Wrapped value is the variable name. ErrorAssignmentToConstant(String, Position), /// Inappropriate property access. Wrapped value is the property name. @@ -181,7 +184,29 @@ impl fmt::Display for EvalAltResult { } Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {d}")?, - Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {s}")?, + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_GET) => { + let prop = &s[FN_GET.len()..]; + write!( + f, + "Property {prop} is not pure and cannot be accessed on a constant" + )? + } + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_SET) => { + let prop = &s[FN_SET.len()..]; + write!(f, "Cannot modify property {prop} of constant")? + } + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_GET => write!( + f, + "Indexer is not pure and cannot be accessed on a constant" + )?, + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_SET => { + write!(f, "Cannot assign to indexer of constant")? + } + Self::ErrorNonPureMethodCallOnConstant(s, ..) => { + write!(f, "Non-pure method {s} cannot be called on constant")? + } + + Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant {s}")?, Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) { ("", e) => write!(f, "Output type incorrect, expecting {e}"), (a, "") => write!(f, "Output type incorrect: {a}"), @@ -296,6 +321,7 @@ impl EvalAltResult { | Self::ErrorIndexNotFound(..) | Self::ErrorModuleNotFound(..) | Self::ErrorDataRace(..) + | Self::ErrorNonPureMethodCallOnConstant(..) | Self::ErrorAssignmentToConstant(..) | Self::ErrorMismatchOutputType(..) | Self::ErrorDotExpr(..) @@ -364,7 +390,7 @@ impl EvalAltResult { | Self::ErrorStackOverflow(..) | Self::ErrorRuntime(..) => (), - Self::ErrorFunctionNotFound(f, ..) => { + Self::ErrorFunctionNotFound(f, ..) | Self::ErrorNonPureMethodCallOnConstant(f, ..) => { map.insert("function".into(), f.into()); } Self::ErrorInFunctionCall(f, s, ..) => { @@ -459,6 +485,7 @@ impl EvalAltResult { | Self::ErrorIndexNotFound(.., pos) | Self::ErrorModuleNotFound(.., pos) | Self::ErrorDataRace(.., pos) + | Self::ErrorNonPureMethodCallOnConstant(.., pos) | Self::ErrorAssignmentToConstant(.., pos) | Self::ErrorMismatchOutputType(.., pos) | Self::ErrorDotExpr(.., pos) @@ -518,6 +545,7 @@ impl EvalAltResult { | Self::ErrorIndexNotFound(.., pos) | Self::ErrorModuleNotFound(.., pos) | Self::ErrorDataRace(.., pos) + | Self::ErrorNonPureMethodCallOnConstant(.., pos) | Self::ErrorAssignmentToConstant(.., pos) | Self::ErrorMismatchOutputType(.., pos) | Self::ErrorDotExpr(.., pos) From 25fde7dbed08ad3174c3d77aa017b799dad967d8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 22:08:47 +0800 Subject: [PATCH 063/116] Fix tests. --- codegen/Cargo.toml | 4 ++-- src/eval/target.rs | 11 +++++++++++ src/func/call.rs | 6 +++++- tests/constants.rs | 4 ++-- tests/plugins.rs | 2 +- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index d384c210..c1efde59 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "1.4.2" +version = "1.4.3" edition = "2018" resolver = "2" authors = ["jhwgh1968", "Stephen Chung"] @@ -22,5 +22,5 @@ syn = { version = "1.0", features = ["full", "parsing", "printing", "proc-macro" quote = "1" [dev-dependencies] -rhai = { path = "..", version = "1.6", features = ["metadata"] } +rhai = { path = "..", version = "1.11", features = ["metadata"] } trybuild = "1" diff --git a/src/eval/target.rs b/src/eval/target.rs index a12aa96c..c48aa62a 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -239,6 +239,17 @@ impl<'a> Target<'a> { _ => None, } } + /// Convert a shared or reference [`Target`] into a target with an owned value. + #[inline(always)] + #[must_use] + pub fn into_owned(self) -> Self { + match self { + Self::RefMut(r) => Self::TempValue(r.clone()), + #[cfg(not(feature = "no_closure"))] + Self::SharedValue { value, .. } => Self::TempValue(value), + _ => self, + } + } /// Get the source [`Dynamic`] of the [`Target`]. #[allow(dead_code)] #[inline] diff --git a/src/func/call.rs b/src/func/call.rs index 758767fb..ae4298fe 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1234,9 +1234,13 @@ impl Engine { .map(|(value, ..)| arg_values.push(value.flatten())) })?; - let (target, _pos) = + let (mut target, _pos) = self.search_namespace(scope, global, lib, this_ptr, first_expr, level)?; + if target.is_read_only() { + target = target.into_owned(); + } + self.track_operation(global, _pos)?; #[cfg(not(feature = "no_closure"))] diff --git a/tests/constants.rs b/tests/constants.rs index b7df1b3a..bc49e8ee 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -87,7 +87,7 @@ fn test_constant_mut() -> Result<(), Box> { " ) .expect_err("should error"), - EvalAltResult::ErrorAssignmentToConstant(..) + EvalAltResult::ErrorNonPureMethodCallOnConstant(..) )); let mut scope = Scope::new(); @@ -120,7 +120,7 @@ fn test_constant_mut() -> Result<(), Box> { *engine .run_with_scope(&mut scope, "MY_NUMBER.value = 42;") .expect_err("should error"), - EvalAltResult::ErrorAssignmentToConstant(..) + EvalAltResult::ErrorNonPureMethodCallOnConstant(..) )); Ok(()) diff --git a/tests/plugins.rs b/tests/plugins.rs index f5c8d966..ab752bc3 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -119,7 +119,7 @@ fn test_plugins_package() -> Result<(), Box> { assert!( matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").expect_err("should error"), - EvalAltResult::ErrorAssignmentToConstant(x, ..) if x == "array") + EvalAltResult::ErrorNonPureMethodCallOnConstant(x, ..) if x == "test") ) } From 6de493c8c2167edcd1d08747c9f79a025a022df3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 22:25:18 +0800 Subject: [PATCH 064/116] Fix builds. --- src/types/error.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/types/error.rs b/src/types/error.rs index e18414de..7991c44f 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,6 +1,5 @@ //! Module containing error definitions for the evaluation process. -use crate::engine::{FN_GET, FN_IDX_GET, FN_IDX_SET, FN_SET}; use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; #[cfg(feature = "no_std")] use core_error::Error; @@ -184,22 +183,32 @@ impl fmt::Display for EvalAltResult { } Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {d}")?, - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_GET) => { - let prop = &s[FN_GET.len()..]; + #[cfg(not(feature = "no_object"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) + if s.starts_with(crate::engine::FN_GET) => + { + let prop = &s[crate::engine::FN_GET.len()..]; write!( f, "Property {prop} is not pure and cannot be accessed on a constant" )? } - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_SET) => { - let prop = &s[FN_SET.len()..]; + #[cfg(not(feature = "no_object"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) + if s.starts_with(crate::engine::FN_SET) => + { + let prop = &s[crate::engine::FN_SET.len()..]; write!(f, "Cannot modify property {prop} of constant")? } - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_GET => write!( - f, - "Indexer is not pure and cannot be accessed on a constant" - )?, - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_SET => { + #[cfg(not(feature = "no_index"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => { + write!( + f, + "Indexer is not pure and cannot be accessed on a constant" + )? + } + #[cfg(not(feature = "no_index"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => { write!(f, "Cannot assign to indexer of constant")? } Self::ErrorNonPureMethodCallOnConstant(s, ..) => { From 6ce1dae11046d816f67eb7a812fe6777e6ea05d2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 10:27:39 +0800 Subject: [PATCH 065/116] Simplify integer bits iterator. --- src/packages/iter_basic.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 4473cfbb..163f93a4 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -122,7 +122,7 @@ impl FusedIterator for StepRange {} // Bit-field iterator with step #[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct BitRange(INT, INT, usize); +pub struct BitRange(INT, usize); impl BitRange { pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf { @@ -138,7 +138,7 @@ impl BitRange { len as usize }; - Ok(Self(value, 1 << from, len)) + Ok(Self(value >> from, len)) } } @@ -146,19 +146,19 @@ impl Iterator for BitRange { type Item = bool; fn next(&mut self) -> Option { - if self.2 == 0 { + if self.1 == 0 { None } else { - let r = (self.0 & self.1) != 0; - self.1 <<= 1; - self.2 -= 1; + let r = (self.0 & 0x0001) != 0; + self.0 >>= 1; + self.1 -= 1; Some(r) } } #[inline(always)] fn size_hint(&self) -> (usize, Option) { - (self.2, Some(self.2)) + (self.1, Some(self.1)) } } @@ -167,7 +167,7 @@ impl FusedIterator for BitRange {} impl ExactSizeIterator for BitRange { #[inline(always)] fn len(&self) -> usize { - self.2 + self.1 } } From 6af66d3ed384b222148d66938c4b3e2b9022cc2a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 10:40:07 +0800 Subject: [PATCH 066/116] Code cleanup. --- src/ast/expr.rs | 2 +- src/bin/rhai-repl.rs | 8 +------ src/eval/chaining.rs | 48 +++++++++++++++++++------------------- src/eval/data_check.rs | 1 + src/packages/time_basic.rs | 2 +- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 7a9e1cae..88bac79b 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -609,7 +609,7 @@ impl Expr { let mut s = SmartString::new_const(); for segment in x.iter() { let v = segment.get_literal_value().unwrap(); - write!(&mut s, "{}", v).unwrap(); + write!(&mut s, "{v}").unwrap(); } s.into() } diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index ea452269..71801d82 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -309,13 +309,7 @@ fn main() { } // Register sample functions - engine - .register_global_module(exported_module!(sample_functions).into()) - .register_get_set( - "test", - |x: &mut INT| *x % 2 == 0, - |x: &mut INT, y: bool| if y { *x *= 2 } else { *x /= 2 }, - ); + engine.register_global_module(exported_module!(sample_functions).into()); // Create scope let mut scope = Scope::new(); diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 78acb0ff..dbf40e3a 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -45,8 +45,8 @@ impl Engine { target: &mut Target, root: (&str, Position), _parent: &Expr, + parent_options: ASTFlags, rhs: &Expr, - _parent_options: ASTFlags, idx_values: &mut FnArgsVec, chain_type: ChainType, level: usize, @@ -61,7 +61,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] ChainType::Indexing => { // Check for existence with the null conditional operator - if _parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() { + if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() { return Ok((Dynamic::UNIT, false)); } @@ -70,7 +70,7 @@ impl Engine { match rhs { // xxx[idx].expr... | xxx[idx][expr]... Expr::Dot(x, options, x_pos) | Expr::Index(x, options, x_pos) - if !_parent_options.contains(ASTFlags::BREAK) => + if !parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; @@ -88,8 +88,8 @@ impl Engine { let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, obj_ptr, root, rhs, &x.rhs, - *options, idx_values, rhs_chain, level, new_val, + global, caches, lib, this_ptr, obj_ptr, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, level, new_val, ) { Ok((result, true)) if is_obj_temp_val => { (Some(obj.take_or_clone()), (result, true)) @@ -190,7 +190,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] ChainType::Dotting => { // Check for existence with the Elvis operator - if _parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() { + if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() { return Ok((Dynamic::UNIT, false)); } @@ -407,7 +407,7 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val_target, root, rhs, &x.rhs, *options, + global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -455,8 +455,8 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val, root, rhs, &x.rhs, - *options, idx_values, rhs_chain, level, new_val, + global, caches, lib, this_ptr, val, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -525,8 +525,8 @@ impl Engine { let val = &mut val.into(); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val, root, rhs, &x.rhs, - *options, idx_values, rhs_chain, level, new_val, + global, caches, lib, this_ptr, val, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(pos)) } @@ -612,7 +612,7 @@ impl Engine { let root = (x.3.as_str(), *var_pos); self.eval_dot_index_chain_helper( - global, caches, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values, + global, caches, lib, &mut None, obj_ptr, root, expr, options, rhs, idx_values, chain_type, level, new_val, ) } @@ -627,7 +627,7 @@ impl Engine { let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values, + global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, chain_type, level, new_val, ) } @@ -646,7 +646,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, parent_options: ASTFlags, - _parent_chain_type: ChainType, + parent_chain_type: ChainType, idx_values: &mut FnArgsVec, level: usize, ) -> RhaiResultOf<()> { @@ -655,7 +655,7 @@ impl Engine { match expr { #[cfg(not(feature = "no_object"))] Expr::MethodCall(x, ..) - if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => + if parent_chain_type == ChainType::Dotting && !x.is_qualified() => { for arg_expr in &x.args { idx_values.push( @@ -666,12 +666,12 @@ impl Engine { } } #[cfg(not(feature = "no_object"))] - Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => { + Expr::MethodCall(..) if parent_chain_type == ChainType::Dotting => { unreachable!("function call in dot chain should not be namespace-qualified") } #[cfg(not(feature = "no_object"))] - Expr::Property(..) if _parent_chain_type == ChainType::Dotting => (), + Expr::Property(..) if parent_chain_type == ChainType::Dotting => (), Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"), Expr::Index(x, options, ..) | Expr::Dot(x, options, ..) @@ -684,12 +684,12 @@ impl Engine { // Evaluate in left-to-right order match lhs { #[cfg(not(feature = "no_object"))] - Expr::Property(..) if _parent_chain_type == ChainType::Dotting => (), + Expr::Property(..) if parent_chain_type == ChainType::Dotting => (), Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"), #[cfg(not(feature = "no_object"))] Expr::MethodCall(x, ..) - if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => + if parent_chain_type == ChainType::Dotting && !x.is_qualified() => { for arg_expr in &x.args { _arg_values.push( @@ -702,15 +702,15 @@ impl Engine { } } #[cfg(not(feature = "no_object"))] - Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => { + Expr::MethodCall(..) if parent_chain_type == ChainType::Dotting => { unreachable!("function call in dot chain should not be namespace-qualified") } #[cfg(not(feature = "no_object"))] - expr if _parent_chain_type == ChainType::Dotting => { + expr if parent_chain_type == ChainType::Dotting => { unreachable!("invalid dot expression: {:?}", expr); } #[cfg(not(feature = "no_index"))] - _ if _parent_chain_type == ChainType::Indexing => { + _ if parent_chain_type == ChainType::Indexing => { _arg_values.push( self.eval_expr(scope, global, caches, lib, this_ptr, lhs, level)? .flatten(), @@ -733,11 +733,11 @@ impl Engine { } #[cfg(not(feature = "no_object"))] - _ if _parent_chain_type == ChainType::Dotting => { + _ if parent_chain_type == ChainType::Dotting => { unreachable!("invalid dot expression: {:?}", expr); } #[cfg(not(feature = "no_index"))] - _ if _parent_chain_type == ChainType::Indexing => idx_values.push( + _ if parent_chain_type == ChainType::Indexing => idx_values.push( self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)? .flatten(), ), diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 9195f632..abda0e45 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -110,6 +110,7 @@ impl Engine { } /// Check whether the size of a [`Dynamic`] is within limits. + #[inline] pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { // If no data size limits, just return if !self.has_data_size_limit() { diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index cf7f3dc4..59e198b8 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -2,7 +2,7 @@ use super::arithmetic::make_err as make_arithmetic_err; use crate::plugin::*; -use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT}; +use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; From c14fbdb14da7e16bef240496335d4c0cc1425ca5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 12:09:18 +0800 Subject: [PATCH 067/116] Add loop expressions. --- CHANGELOG.md | 7 +++++++ src/api/options.rs | 28 ++++++++++++++++++------- src/ast/stmt.rs | 6 +++--- src/eval/stmt.rs | 29 ++++++++++++++++---------- src/optimizer.rs | 49 ++++---------------------------------------- src/parser.rs | 38 +++++++++++++++++++++++++++++++--- src/types/error.rs | 2 +- tests/expressions.rs | 9 +++++--- tests/for.rs | 16 +++++++++++++++ tests/while_loop.rs | 35 +++++++++++++++++++++++++++++++ 10 files changed, 146 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49aa5966..b6f6d5c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Bug fixes * `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications. * `import` statements inside `eval` no longer cause errors in subsequent code. * Functions marked `global` in `import`ed modules with no alias names now work properly. +* Incorrect loop optimizations that are too aggressive (e.g. unrolling a `do { ... } until true` with a `break` statement inside) and cause crashes are removed. Speed Improvements ------------------ @@ -19,6 +20,12 @@ Speed Improvements New features ------------ +### Loop expressions + +* Loops (such as `loop`, `do`, `while` and `for`) can now act as _expressions_, with the `break` statement returning an optional value. +* Normal loops return `()` as the value. +* Loop expressions can be enabled/disabled via `Engine::set_allow_loop_expressions` + ### Stable hashing * It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. diff --git a/src/api/options.rs b/src/api/options.rs index d5596cfd..42b57456 100644 --- a/src/api/options.rs +++ b/src/api/options.rs @@ -12,23 +12,25 @@ bitflags! { const IF_EXPR = 0b_0000_0000_0001; /// Is `switch` expression allowed? const SWITCH_EXPR = 0b_0000_0000_0010; + /// Are loop expressions allowed? + const LOOP_EXPR = 0b_0000_0000_0100; /// Is statement-expression allowed? - const STMT_EXPR = 0b_0000_0000_0100; + const STMT_EXPR = 0b_0000_0000_1000; /// Is anonymous function allowed? #[cfg(not(feature = "no_function"))] - const ANON_FN = 0b_0000_0000_1000; + const ANON_FN = 0b_0000_0001_0000; /// Is looping allowed? - const LOOPING = 0b_0000_0001_0000; + const LOOPING = 0b_0000_0010_0000; /// Is variables shadowing allowed? - const SHADOW = 0b_0000_0010_0000; + const SHADOW = 0b_0000_0100_0000; /// Strict variables mode? - const STRICT_VAR = 0b_0000_0100_0000; + const STRICT_VAR = 0b_0000_1000_0000; /// Raise error if an object map property does not exist? /// Returns `()` if `false`. #[cfg(not(feature = "no_object"))] - const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0000_1000_0000; + const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0001_0000_0000; /// Fast operators mode? - const FAST_OPS = 0b_0001_0000_0000; + const FAST_OPS = 0b_0010_0000_0000; } } @@ -81,6 +83,18 @@ impl Engine { pub fn set_allow_switch_expression(&mut self, enable: bool) { self.options.set(LangOptions::SWITCH_EXPR, enable); } + /// Are loop expressions allowed? + /// Default is `true`. + #[inline(always)] + #[must_use] + pub const fn allow_loop_expressions(&self) -> bool { + self.options.contains(LangOptions::LOOP_EXPR) + } + /// Set whether loop expressions are allowed. + #[inline(always)] + pub fn set_allow_loop_expressions(&mut self, enable: bool) { + self.options.set(LangOptions::LOOP_EXPR, enable); + } /// Is statement-expression allowed? /// Default is `true`. #[inline(always)] diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 284d0133..ed6c51a3 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -581,14 +581,14 @@ pub enum Stmt { TryCatch(Box, Position), /// [expression][Expr] Expr(Box), - /// `continue`/`break` + /// `continue`/`break` expr /// /// ### Flags /// /// * [`NONE`][ASTFlags::NONE] = `continue` /// * [`BREAK`][ASTFlags::BREAK] = `break` - BreakLoop(ASTFlags, Position), - /// `return`/`throw` + BreakLoop(Option>, ASTFlags, Position), + /// `return`/`throw` expr /// /// ### Flags /// diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 770d4174..ae933564 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -493,7 +493,7 @@ impl Engine { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + ERR::LoopBreak(true, value, ..) => break Ok(value), _ => break Err(err), }, } @@ -524,7 +524,7 @@ impl Engine { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + ERR::LoopBreak(true, value, ..) => break Ok(value), _ => break Err(err), }, } @@ -547,7 +547,7 @@ impl Engine { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, ..) => continue, - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + ERR::LoopBreak(true, value, ..) => break Ok(value), _ => break Err(err), }, } @@ -614,7 +614,7 @@ impl Engine { let loop_result = func(iter_obj) .enumerate() - .try_for_each(|(x, iter_value)| { + .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { // Increment counter if counter_index < usize::MAX { // As the variable increments from 0, this should always work @@ -644,26 +644,26 @@ impl Engine { self.track_operation(global, statements.position())?; if statements.is_empty() { - return Ok(()); + return Ok(Dynamic::UNIT); } self.eval_stmt_block( scope, global, caches, lib, this_ptr, statements, true, level, ) - .map(|_| ()) + .map(|_| Dynamic::UNIT) .or_else(|err| match *err { - ERR::LoopBreak(false, ..) => Ok(()), + ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), _ => Err(err), }) }) .or_else(|err| match *err { - ERR::LoopBreak(true, ..) => Ok(()), + ERR::LoopBreak(true, value, ..) => Ok(value), _ => Err(err), }); scope.rewind(orig_scope_len); - loop_result.map(|_| Dynamic::UNIT) + loop_result } else { Err(ERR::ErrorFor(expr.start_position()).into()) } @@ -673,8 +673,15 @@ impl Engine { } // Continue/Break statement - Stmt::BreakLoop(options, pos) => { - Err(ERR::LoopBreak(options.contains(ASTFlags::BREAK), *pos).into()) + Stmt::BreakLoop(expr, options, pos) => { + let is_break = options.contains(ASTFlags::BREAK); + + if let Some(ref expr) = expr { + self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .and_then(|v| ERR::LoopBreak(is_break, v, *pos).into()) + } else { + Err(ERR::LoopBreak(is_break, Dynamic::UNIT, *pos).into()) + } } // Try/Catch statement diff --git a/src/optimizer.rs b/src/optimizer.rs index bd51a7c4..08abb8f2 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -8,7 +8,7 @@ use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::hashing::get_hasher; -use crate::tokenizer::{Span, Token}; +use crate::tokenizer::Token; use crate::types::dynamic::AccessMode; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier, @@ -785,50 +785,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b *condition = Expr::Unit(*pos); } **body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false); - - if body.len() == 1 { - match body[0] { - // while expr { break; } -> { expr; } - Stmt::BreakLoop(options, pos) if options.contains(ASTFlags::BREAK) => { - // Only a single break statement - turn into running the guard expression once - state.set_dirty(); - if condition.is_unit() { - *stmt = Stmt::Noop(pos); - } else { - let mut statements = vec![Stmt::Expr(mem::take(condition).into())]; - if preserve_result { - statements.push(Stmt::Noop(pos)); - } - *stmt = (statements, Span::new(pos, Position::NONE)).into(); - }; - } - _ => (), - } - } - } - // do { block } until true -> { block } - Stmt::Do(x, options, ..) - if matches!(x.0, Expr::BoolConstant(true, ..)) - && options.contains(ASTFlags::NEGATED) => - { - state.set_dirty(); - *stmt = ( - optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false), - x.1.span(), - ) - .into(); - } - // do { block } while false -> { block } - Stmt::Do(x, options, ..) - if matches!(x.0, Expr::BoolConstant(false, ..)) - && !options.contains(ASTFlags::NEGATED) => - { - state.set_dirty(); - *stmt = ( - optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false), - x.1.span(), - ) - .into(); } // do { block } while|until expr Stmt::Do(x, ..) => { @@ -916,6 +872,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b } } + // break expr; + Stmt::BreakLoop(Some(ref mut expr), ..) => optimize_expr(expr, state, false), + // return expr; Stmt::Return(Some(ref mut expr), ..) => optimize_expr(expr, state, false), diff --git a/src/parser.rs b/src/parser.rs index e2bcb181..9ca3a8a0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1380,6 +1380,23 @@ impl Engine { self.parse_if(input, state, lib, settings.level_up())? .into(), )), + // Loops are allowed to act as expressions + Token::While | Token::Loop if settings.options.contains(LangOptions::LOOP_EXPR) => { + Expr::Stmt(Box::new( + self.parse_while_loop(input, state, lib, settings.level_up())? + .into(), + )) + } + Token::Do if settings.options.contains(LangOptions::LOOP_EXPR) => Expr::Stmt(Box::new( + self.parse_do(input, state, lib, settings.level_up())? + .into(), + )), + Token::For if settings.options.contains(LangOptions::LOOP_EXPR) => { + Expr::Stmt(Box::new( + self.parse_for(input, state, lib, settings.level_up())? + .into(), + )) + } // Switch statement is allowed to act as expressions Token::Switch if settings.options.contains(LangOptions::SWITCH_EXPR) => { Expr::Stmt(Box::new( @@ -3411,11 +3428,26 @@ impl Engine { Token::Continue if self.allow_looping() && settings.is_breakable => { let pos = eat_token(input, Token::Continue); - Ok(Stmt::BreakLoop(ASTFlags::NONE, pos)) + Ok(Stmt::BreakLoop(None, ASTFlags::NONE, pos)) } Token::Break if self.allow_looping() && settings.is_breakable => { let pos = eat_token(input, Token::Break); - Ok(Stmt::BreakLoop(ASTFlags::BREAK, pos)) + + let expr = match input.peek().expect(NEVER_ENDS) { + // `break` at + (Token::EOF, ..) => None, + // `break` at end of block + (Token::RightBrace, ..) => None, + // `break;` + (Token::SemiColon, ..) => None, + // `break` with expression + _ => Some( + self.parse_expr(input, state, lib, settings.level_up())? + .into(), + ), + }; + + Ok(Stmt::BreakLoop(expr, ASTFlags::BREAK, pos)) } Token::Continue | Token::Break if self.allow_looping() => { Err(PERR::LoopBreak.into_err(token_pos)) @@ -3840,7 +3872,7 @@ impl Engine { let mut functions = StraightHashMap::default(); let mut options = self.options; - options.remove(LangOptions::STMT_EXPR); + options.remove(LangOptions::STMT_EXPR | LangOptions::LOOP_EXPR); #[cfg(not(feature = "no_function"))] options.remove(LangOptions::ANON_FN); diff --git a/src/types/error.rs b/src/types/error.rs index 7991c44f..906f32c9 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -117,7 +117,7 @@ pub enum EvalAltResult { /// Breaking out of loops - not an error if within a loop. /// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement). /// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement). - LoopBreak(bool, Position), + LoopBreak(bool, Dynamic, Position), /// Not an error: Value returned from a script via the `return` keyword. /// Wrapped value is the result value. Return(Dynamic, Position), diff --git a/tests/expressions.rs b/tests/expressions.rs index 4d0b4888..ed5e3a16 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -55,10 +55,13 @@ fn test_expressions() -> Result<(), Box> { ) .is_err()); - assert!(engine.eval_expression::<()>("40 + 2;").is_err()); - assert!(engine.eval_expression::<()>("40 + { 2 }").is_err()); - assert!(engine.eval_expression::<()>("x = 42").is_err()); + assert!(engine.compile_expression("40 + 2;").is_err()); + assert!(engine.compile_expression("40 + { 2 }").is_err()); + assert!(engine.compile_expression("x = 42").is_err()); assert!(engine.compile_expression("let x = 42").is_err()); + assert!(engine + .compile_expression("do { break 42; } while true") + .is_err()); engine.compile("40 + { let x = 2; x }")?; diff --git a/tests/for.rs b/tests/for.rs index 92e68af1..af2eed86 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -231,6 +231,22 @@ fn test_for_loop() -> Result<(), Box> { ); } + assert_eq!( + engine.eval::( + r#" + let a = [123, 999, 42, 0, true, "hello", "world!", 987.6543]; + + for (item, count) in a { + switch item.type_of() { + "i64" if item.is_even => break count, + "f64" if item.to_int().is_even => break count, + } + } + "# + )?, + 2 + ); + Ok(()) } diff --git a/tests/while_loop.rs b/tests/while_loop.rs index 4e18a371..ebcdb8a0 100644 --- a/tests/while_loop.rs +++ b/tests/while_loop.rs @@ -22,6 +22,22 @@ fn test_while() -> Result<(), Box> { 6 ); + assert_eq!( + engine.eval::( + " + let x = 0; + + while x < 10 { + x += 1; + if x > 5 { break x * 2; } + if x > 3 { continue; } + x += 3; + } + ", + )?, + 12 + ); + Ok(()) } @@ -46,6 +62,25 @@ fn test_do() -> Result<(), Box> { )?, 6 ); + assert_eq!( + engine.eval::( + " + let x = 0; + + do { + x += 1; + if x > 5 { break x * 2; } + if x > 3 { continue; } + x += 3; + } while x < 10; + ", + )?, + 12 + ); + + engine.run("do {} while false")?; + + assert_eq!(engine.eval::("do { break 42; } while false")?, 42); Ok(()) } From 68bd84417aa79c0427a6e89a1c430cebc162d83a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 12:56:35 +0800 Subject: [PATCH 068/116] Fix tests. --- src/engine.rs | 2 ++ tests/for.rs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine.rs b/src/engine.rs index 8071b9de..0afadf75 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -239,8 +239,10 @@ impl Engine { engine.print = Box::new(|s| println!("{s}")); engine.debug = 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}"), }); } diff --git a/tests/for.rs b/tests/for.rs index af2eed86..63c8ae92 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -231,10 +231,12 @@ fn test_for_loop() -> Result<(), Box> { ); } + #[cfg(not(feature = "no_index"))] + #[cfg(not(feature = "no_object"))] assert_eq!( engine.eval::( r#" - let a = [123, 999, 42, 0, true, "hello", "world!", 987.6543]; + let a = [123, 999, 42, 0, true, "hello", "world!"]; for (item, count) in a { switch item.type_of() { From de81941c2c2c5036d24328e304413bb485af6ccf Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 12:57:58 +0800 Subject: [PATCH 069/116] Fix test. --- tests/for.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/for.rs b/tests/for.rs index 63c8ae92..c8353b51 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -233,10 +233,12 @@ fn test_for_loop() -> Result<(), Box> { #[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "no_float"))] assert_eq!( engine.eval::( r#" - let a = [123, 999, 42, 0, true, "hello", "world!"]; + let a = [123, 999, 42, 0, true, "hello", "world!", 987.654]; for (item, count) in a { switch item.type_of() { From 905b0b83325b5d48f97a7c52145b7a87610a625b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 12:58:38 +0800 Subject: [PATCH 070/116] Fix test. --- tests/for.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/for.rs b/tests/for.rs index c8353b51..a379a977 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -233,7 +233,6 @@ fn test_for_loop() -> Result<(), Box> { #[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_object"))] - #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "no_float"))] assert_eq!( engine.eval::( @@ -242,8 +241,8 @@ fn test_for_loop() -> Result<(), Box> { for (item, count) in a { switch item.type_of() { - "i64" if item.is_even => break count, - "f64" if item.to_int().is_even => break count, + "i64" | "i32 if item.is_even => break count, + "f64" | "f32 if item.to_int().is_even => break count, } } "# From d0998a44b9fbcaecbc506823abd63659627473e9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 14:12:09 +0800 Subject: [PATCH 071/116] Fix test again. --- tests/for.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/for.rs b/tests/for.rs index a379a977..48139e70 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -241,8 +241,8 @@ fn test_for_loop() -> Result<(), Box> { for (item, count) in a { switch item.type_of() { - "i64" | "i32 if item.is_even => break count, - "f64" | "f32 if item.to_int().is_even => break count, + "i64" | "i32" if item.is_even => break count, + "f64" | "f32" if item.to_int().is_even => break count, } } "# From 4100e6da64f6a152335d720d40d61f429e8260e7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 14:12:18 +0800 Subject: [PATCH 072/116] Use ImmutableString for source. --- src/api/eval.rs | 2 +- src/api/run.rs | 2 +- src/ast/ast.rs | 52 ++++++++++++++++++++------------------- src/eval/cache.rs | 4 +-- src/eval/debugger.rs | 33 +++++++++++++------------ src/eval/eval_context.rs | 6 +---- src/eval/global_state.rs | 44 +++++++++++++++++---------------- src/eval/stmt.rs | 4 +-- src/func/call.rs | 39 +++++++++-------------------- src/func/script.rs | 2 +- src/module/mod.rs | 32 +++++++++++++----------- src/packages/debugging.rs | 4 +-- tests/modules.rs | 2 +- tests/optimizer.rs | 12 ++++----- 14 files changed, 112 insertions(+), 126 deletions(-) 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(()) From 8e35f9847733df6210d67b4daf210935cd129e17 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 14:27:30 +0800 Subject: [PATCH 073/116] Fix build. --- src/ast/ast.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/ast.rs b/src/ast/ast.rs index aa18b3ac..9c186e67 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -77,7 +77,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), @@ -114,7 +114,7 @@ impl AST { pub(crate) 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, From 91415b9750c77abca2831b8cfe3cf13867cbfd63 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 14:59:20 +0800 Subject: [PATCH 074/116] Use ImmutableString in more places. --- src/api/definitions/mod.rs | 4 +-- src/ast/expr.rs | 2 +- src/bin/rhai-dbg.rs | 4 +-- src/eval/debugger.rs | 31 ++++++++----------- src/func/call.rs | 2 +- src/module/mod.rs | 2 +- src/packages/debugging.rs | 2 +- src/packages/fn_basic.rs | 2 +- src/tests.rs | 2 +- src/types/fn_ptr.rs | 61 ++++++++------------------------------ src/types/scope.rs | 18 ++++++----- 11 files changed, 45 insertions(+), 85 deletions(-) diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 18d9c992..3ee43af0 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -442,8 +442,8 @@ impl Module { if f.access != FnAccess::Private { #[cfg(not(feature = "no_custom_syntax"))] - let operator = def.engine.custom_keywords.contains_key(&f.name) - || (!f.name.contains('$') && !is_valid_function_name(&f.name)); + let operator = def.engine.custom_keywords.contains_key(f.name.as_str()) + || (!f.name.contains('$') && !is_valid_function_name(f.name.as_str())); #[cfg(feature = "no_custom_syntax")] let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 88bac79b..863299c8 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -619,7 +619,7 @@ impl Expr { if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR => { if let Self::StringConstant(ref s, ..) = x.args[0] { - FnPtr::new(s).ok()?.into() + FnPtr::new(s.clone()).ok()?.into() } else { return None; } diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index 5649c0c1..04284050 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -516,7 +516,7 @@ fn debug_callback( if range.contains(&n) { let bp = rhai::debugger::BreakPoint::AtPosition { - source: source.unwrap_or("").into(), + source: source.map(|s| s.into()), pos: Position::new(n as u16, 0), enabled: true, }; @@ -546,7 +546,7 @@ fn debug_callback( #[cfg(not(feature = "no_position"))] ["break" | "b"] => { let bp = rhai::debugger::BreakPoint::AtPosition { - source: source.unwrap_or("").into(), + source: source.map(|s| s.into()), pos, enabled: true, }; diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 913f66a5..f87e4a81 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -4,8 +4,7 @@ use super::{EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{ - Dynamic, Engine, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResultOf, - Scope, + Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -103,12 +102,10 @@ pub enum BreakPoint { /// Break at a particular position under a particular source. /// /// Not available under `no_position`. - /// - /// Source is empty if not available. #[cfg(not(feature = "no_position"))] AtPosition { /// Source (empty if not available) of the break-point. - source: Identifier, + source: Option, /// [Position] of the break-point. pos: Position, /// Is the break-point enabled? @@ -117,14 +114,14 @@ pub enum BreakPoint { /// Break at a particular function call. AtFunctionName { /// Function name. - name: Identifier, + name: ImmutableString, /// Is the break-point enabled? enabled: bool, }, /// Break at a particular function call with a particular number of arguments. AtFunctionCall { /// Function name. - name: Identifier, + name: ImmutableString, /// Number of arguments. args: usize, /// Is the break-point enabled? @@ -136,7 +133,7 @@ pub enum BreakPoint { #[cfg(not(feature = "no_object"))] AtProperty { /// Property name. - name: Identifier, + name: ImmutableString, /// Is the break-point enabled? enabled: bool, }, @@ -151,7 +148,7 @@ impl fmt::Display for BreakPoint { pos, enabled, } => { - if !source.is_empty() { + if let Some(ref source) = source { write!(f, "{source} ")?; } write!(f, "@ {pos:?}")?; @@ -226,7 +223,7 @@ impl BreakPoint { #[derive(Debug, Clone, Hash)] pub struct CallStackFrame { /// Function name. - pub fn_name: Identifier, + pub fn_name: ImmutableString, /// Copies of function call arguments, if any. pub args: crate::StaticVec, /// Source of the function. @@ -296,7 +293,7 @@ impl Debugger { #[inline(always)] pub(crate) fn push_call_stack_frame( &mut self, - fn_name: impl Into, + fn_name: ImmutableString, args: crate::StaticVec, source: Option, pos: Position, @@ -331,7 +328,7 @@ impl Debugger { } /// Returns the first break-point triggered by a particular [`AST` Node][ASTNode]. #[must_use] - pub fn is_break_point(&self, src: &str, node: ASTNode) -> Option { + pub fn is_break_point(&self, src: Option<&str>, node: ASTNode) -> Option { let _src = src; self.break_points() @@ -343,11 +340,12 @@ impl Debugger { BreakPoint::AtPosition { pos, .. } if pos.is_none() => false, #[cfg(not(feature = "no_position"))] BreakPoint::AtPosition { source, pos, .. } if pos.is_beginning_of_line() => { - node.position().line().unwrap_or(0) == pos.line().unwrap() && _src == source + node.position().line().unwrap_or(0) == pos.line().unwrap() + && _src == source.as_ref().map(|s| s.as_str()) } #[cfg(not(feature = "no_position"))] BreakPoint::AtPosition { source, pos, .. } => { - node.position() == *pos && _src == source + node.position() == *pos && _src == source.as_ref().map(|s| s.as_str()) } BreakPoint::AtFunctionName { name, .. } => match node { ASTNode::Expr(Expr::FnCall(x, ..)) | ASTNode::Stmt(Stmt::FnCall(x, ..)) => { @@ -490,10 +488,7 @@ impl Engine { let event = match event { Some(e) => e, - None => match global - .debugger - .is_break_point(global.source().unwrap_or(""), node) - { + None => match global.debugger.is_break_point(global.source(), node) { Some(bp) => DebuggerEvent::BreakPoint(bp), None => return Ok(None), }, diff --git a/src/func/call.rs b/src/func/call.rs index 5c3fbf43..ef5d77e7 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -400,7 +400,7 @@ impl Engine { #[cfg(feature = "debugging")] if self.debugger.is_some() { global.debugger.push_call_stack_frame( - name, + self.get_interned_string(name), args.iter().map(|v| (*v).clone()).collect(), source.clone().or_else(|| global.source.clone()), pos, diff --git a/src/module/mod.rs b/src/module/mod.rs index 37a0e167..59ead075 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -72,7 +72,7 @@ pub struct FuncInfo { /// Function access mode. pub access: FnAccess, /// Function name. - pub name: Identifier, + pub name: ImmutableString, /// Number of parameters. pub num_params: usize, /// Parameter types (if applicable). diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs index eba64f5e..8a800c1b 100644 --- a/src/packages/debugging.rs +++ b/src/packages/debugging.rs @@ -40,7 +40,7 @@ mod debugging_functions { .iter() .rev() .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { - fn_name != "back_trace" || !args.is_empty() + fn_name.as_str() != "back_trace" || !args.is_empty() }) .map( |frame @ crate::debugger::CallStackFrame { diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 505a08b5..130646c3 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -27,7 +27,7 @@ mod fn_ptr_functions { /// ``` #[rhai_fn(name = "name", get = "name", pure)] pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString { - fn_ptr.fn_name_raw().into() + fn_ptr.fn_name_raw().clone() } /// Return `true` if the function is an anonymous function. diff --git a/src/tests.rs b/src/tests.rs index c91a1e1e..34186402 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -35,7 +35,7 @@ fn check_struct_sizes() { #[cfg(target_pointer_width = "64")] { assert_eq!(size_of::(), 536); - assert_eq!(size_of::(), 80); + assert_eq!(size_of::(), 64); assert_eq!(size_of::(), 56); assert_eq!( size_of::(), diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index ab39e9b9..8c5d4b1f 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -3,7 +3,7 @@ use crate::tokenizer::is_valid_identifier; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, FuncArgs, Identifier, Module, NativeCallContext, Position, RhaiError, + Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError, RhaiResult, RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] @@ -18,7 +18,7 @@ use std::{ /// to be passed onto a function during a call. #[derive(Clone, Hash)] pub struct FnPtr { - name: Identifier, + name: ImmutableString, curry: StaticVec, } @@ -42,13 +42,16 @@ impl fmt::Debug for FnPtr { impl FnPtr { /// Create a new function pointer. #[inline(always)] - pub fn new(name: impl Into) -> RhaiResultOf { + pub fn new(name: impl Into) -> RhaiResultOf { name.into().try_into() } /// Create a new function pointer without checking its parameters. #[inline(always)] #[must_use] - pub(crate) fn new_unchecked(name: impl Into, curry: StaticVec) -> Self { + pub(crate) fn new_unchecked( + name: impl Into, + curry: StaticVec, + ) -> Self { Self { name: name.into(), curry, @@ -63,13 +66,13 @@ impl FnPtr { /// Get the name of the function. #[inline(always)] #[must_use] - pub(crate) const fn fn_name_raw(&self) -> &Identifier { + pub(crate) const fn fn_name_raw(&self) -> &ImmutableString { &self.name } /// Get the underlying data of the function pointer. #[inline(always)] #[must_use] - pub(crate) fn take_data(self) -> (Identifier, StaticVec) { + pub(crate) fn take_data(self) -> (ImmutableString, StaticVec) { (self.name, self.curry) } /// Get the curried arguments. @@ -246,11 +249,11 @@ impl fmt::Display for FnPtr { } } -impl TryFrom for FnPtr { +impl TryFrom for FnPtr { type Error = RhaiError; - #[inline] - fn try_from(value: Identifier) -> RhaiResultOf { + #[inline(always)] + fn try_from(value: ImmutableString) -> RhaiResultOf { if is_valid_identifier(value.chars()) { Ok(Self { name: value, @@ -261,43 +264,3 @@ impl TryFrom for FnPtr { } } } - -impl TryFrom for FnPtr { - type Error = RhaiError; - - #[inline(always)] - fn try_from(value: crate::ImmutableString) -> RhaiResultOf { - let s: Identifier = value.into(); - Self::try_from(s) - } -} - -impl TryFrom for FnPtr { - type Error = RhaiError; - - #[inline(always)] - fn try_from(value: String) -> RhaiResultOf { - let s: Identifier = value.into(); - Self::try_from(s) - } -} - -impl TryFrom> for FnPtr { - type Error = RhaiError; - - #[inline(always)] - fn try_from(value: Box) -> RhaiResultOf { - let s: Identifier = value.into(); - Self::try_from(s) - } -} - -impl TryFrom<&str> for FnPtr { - type Error = RhaiError; - - #[inline(always)] - fn try_from(value: &str) -> RhaiResultOf { - let s: Identifier = value.into(); - Self::try_from(s) - } -} diff --git a/src/types/scope.rs b/src/types/scope.rs index 435042e8..ba699c24 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -1,7 +1,7 @@ //! Module that defines the [`Scope`] type representing a function call-stack scope. use super::dynamic::{AccessMode, Variant}; -use crate::{Dynamic, Identifier}; +use crate::{Dynamic, Identifier, ImmutableString}; use smallvec::SmallVec; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -75,7 +75,7 @@ pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> { /// Name of the entry. names: SmallVec<[Identifier; SCOPE_ENTRIES_INLINED]>, /// Aliases of the entry. - aliases: SmallVec<[Vec; SCOPE_ENTRIES_INLINED]>, + aliases: SmallVec<[Vec; SCOPE_ENTRIES_INLINED]>, /// Phantom to keep the lifetime parameter in order not to break existing code. dummy: PhantomData<&'a ()>, } @@ -125,7 +125,7 @@ impl Clone for Scope<'_> { } impl IntoIterator for Scope<'_> { - type Item = (String, Dynamic, Vec); + type Item = (String, Dynamic, Vec); type IntoIter = Box>; #[must_use] @@ -140,7 +140,7 @@ impl IntoIterator for Scope<'_> { } impl<'a> IntoIterator for &'a Scope<'_> { - type Item = (&'a Identifier, &'a Dynamic, &'a Vec); + type Item = (&'a Identifier, &'a Dynamic, &'a Vec); type IntoIter = Box + 'a>; #[must_use] @@ -669,7 +669,7 @@ impl Scope<'_> { /// Panics if the index is out of bounds. #[cfg(not(feature = "no_module"))] #[inline] - pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: Identifier) -> &mut Self { + pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: ImmutableString) -> &mut Self { let aliases = self.aliases.get_mut(index).unwrap(); if aliases.is_empty() || !aliases.contains(&alias) { aliases.push(alias); @@ -690,11 +690,11 @@ impl Scope<'_> { pub fn set_alias( &mut self, name: impl AsRef + Into, - alias: impl Into, + alias: impl Into, ) { if let Some(index) = self.search(name.as_ref()) { let alias = match alias.into() { - x if x.is_empty() => name.into(), + x if x.is_empty() => name.into().into(), x => x, }; self.add_alias_by_index(index, alias); @@ -727,7 +727,9 @@ impl Scope<'_> { } /// Get an iterator to entries in the [`Scope`]. #[allow(dead_code)] - pub(crate) fn into_iter(self) -> impl Iterator)> { + pub(crate) fn into_iter( + self, + ) -> impl Iterator)> { self.names .into_iter() .zip(self.values.into_iter().zip(self.aliases.into_iter())) From d97f3f7ec44b31b8321077de30d0ee00a6924f62 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 29 Oct 2022 15:17:12 +0800 Subject: [PATCH 075/116] Merge variables in Stmt::Share. --- src/ast/stmt.rs | 13 +++++-------- src/eval/stmt.rs | 34 ++++++++++++++++++---------------- src/parser.rs | 12 +++++++----- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index ed6c51a3..e272e1e9 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -605,19 +605,16 @@ pub enum Stmt { /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] Export(Box<(Ident, Ident)>, Position), - /// Convert a variable to shared. + /// Convert a list of variables to shared. /// /// Not available under `no_closure`. /// /// # Notes /// /// This variant does not map to any language structure. It is currently only used only to - /// convert a normal variable into a shared variable when the variable is _captured_ by a closure. + /// convert normal variables into shared variables when they are _captured_ by a closure. #[cfg(not(feature = "no_closure"))] - Share( - Box<(crate::ImmutableString, Option)>, - Position, - ), + Share(Box, Position)>>), } impl Default for Stmt { @@ -684,7 +681,7 @@ impl Stmt { Self::Export(.., pos) => *pos, #[cfg(not(feature = "no_closure"))] - Self::Share(.., pos) => *pos, + Self::Share(x) => x[0].2, } } /// Override the [position][Position] of this statement. @@ -716,7 +713,7 @@ impl Stmt { Self::Export(.., pos) => *pos = new_pos, #[cfg(not(feature = "no_closure"))] - Self::Share(.., pos) => *pos = new_pos, + Self::Share(x) => x.iter_mut().for_each(|(_, _, pos)| *pos = new_pos), } self diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 4eecbf88..0a302ee6 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -977,23 +977,25 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] - Stmt::Share(x, pos) => { - let (name, index) = &**x; + Stmt::Share(x) => { + x.iter() + .try_for_each(|(name, index, pos)| { + if let Some(index) = index + .map(|n| scope.len() - n.get()) + .or_else(|| scope.search(name)) + { + let val = scope.get_mut_by_index(index); - if let Some(index) = index - .map(|n| scope.len() - n.get()) - .or_else(|| scope.search(name)) - { - let val = scope.get_mut_by_index(index); - - if !val.is_shared() { - // Replace the variable with a shared value. - *val = std::mem::take(val).into_shared(); - } - Ok(Dynamic::UNIT) - } else { - Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()) - } + if !val.is_shared() { + // Replace the variable with a shared value. + *val = std::mem::take(val).into_shared(); + } + Ok(()) + } else { + Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()) + } + }) + .map(|_| Dynamic::UNIT) } _ => unreachable!("statement cannot be evaluated: {:?}", stmt), diff --git a/src/parser.rs b/src/parser.rs index 9ca3a8a0..c2e02aa7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3739,15 +3739,17 @@ impl Engine { // Convert the entire expression into a statement block, then insert the relevant // [`Share`][Stmt::Share] statements. - let mut statements = StaticVec::with_capacity(externals.len() + 1); - statements.extend( + let mut statements = StaticVec::with_capacity(2); + statements.push(Stmt::Share( externals .into_iter() .map(|crate::ast::Ident { name, pos }| { let (index, _) = parent.access_var(&name, lib, pos); - Stmt::Share((name, index).into(), pos) - }), - ); + (name, index, pos) + }) + .collect::>() + .into(), + )); statements.push(Stmt::Expr(expr.into())); Expr::Stmt(crate::ast::StmtBlock::new(statements, pos, Position::NONE).into()) } From c9184db4d27e602b20d414e82f3e05a135592d45 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 30 Oct 2022 15:45:25 +0800 Subject: [PATCH 076/116] Refine native/script code splits. --- src/api/custom_syntax.rs | 2 +- src/ast/expr.rs | 36 ++++---- src/eval/expr.rs | 13 +-- src/func/call.rs | 178 +++++++++++++++++++++------------------ src/func/native.rs | 1 - src/optimizer.rs | 2 +- src/parser.rs | 70 +++++++-------- src/tokenizer.rs | 10 +-- src/types/fn_ptr.rs | 7 +- src/types/parse_error.rs | 2 +- 10 files changed, 163 insertions(+), 158 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 868b9931..7aaad56b 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -273,7 +273,7 @@ impl Engine { .into_err(Position::NONE)); } // Identifier in first position - _ if segments.is_empty() && is_valid_identifier(s.chars()) => { + _ if segments.is_empty() && is_valid_identifier(s) => { // Make it a custom keyword/symbol if it is disabled or reserved if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s)) || token.map_or(false, |v| v.is_reserved()) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 863299c8..f42209b6 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -111,9 +111,11 @@ impl CustomExpr { pub struct FnCallHashes { /// Pre-calculated hash for a script-defined function (zero if native functions only). #[cfg(not(feature = "no_function"))] - pub script: u64, + script: u64, /// Pre-calculated hash for a native Rust function with no parameter types. - pub native: u64, + /// + /// This hash can never be zero. + native: u64, } impl fmt::Debug for FnCallHashes { @@ -148,7 +150,7 @@ impl From for FnCallHashes { impl FnCallHashes { /// Create a [`FnCallHashes`] with only the native Rust hash. - #[inline] + #[inline(always)] #[must_use] pub const fn from_native(hash: u64) -> Self { Self { @@ -158,7 +160,7 @@ impl FnCallHashes { } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. - #[inline] + #[inline(always)] #[must_use] pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { @@ -167,16 +169,29 @@ impl FnCallHashes { native: if native == 0 { ALT_ZERO_HASH } else { native }, } } - /// Is this [`FnCallHashes`] native Rust only? + /// Is this [`FnCallHashes`] native-only? #[inline(always)] #[must_use] pub const fn is_native_only(&self) -> bool { #[cfg(not(feature = "no_function"))] return self.script == 0; - #[cfg(feature = "no_function")] return true; } + /// Get the native hash. + #[inline(always)] + #[must_use] + pub const fn native(&self) -> u64 { + self.native + } + /// Get the script hash. + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub const fn script(&self) -> u64 { + assert!(self.script != 0); + self.script + } } /// _(internals)_ A function call. @@ -196,9 +211,6 @@ pub struct FnCallExpr { pub capture_parent_scope: bool, /// Is this function call a native operator? pub operator_token: Option, - /// Can this function call be a scripted function? - #[cfg(not(feature = "no_function"))] - pub can_be_script: bool, /// [Position] of the function name. pub pos: Position, } @@ -221,10 +233,6 @@ impl fmt::Debug for FnCallExpr { if self.capture_parent_scope { ff.field("capture_parent_scope", &self.capture_parent_scope); } - #[cfg(not(feature = "no_function"))] - if self.can_be_script { - ff.field("can_be_script", &self.can_be_script); - } ff.field("pos", &self.pos); ff.finish() } @@ -691,8 +699,6 @@ impl Expr { args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, operator_token: None, - #[cfg(not(feature = "no_function"))] - can_be_script: true, pos, } .into(), diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 5afd07d9..17c2d82d 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -223,16 +223,9 @@ impl Engine { hashes, args, operator_token, - #[cfg(not(feature = "no_function"))] - can_be_script, .. } = expr; - #[cfg(not(feature = "no_function"))] - let native = !can_be_script; - #[cfg(feature = "no_function")] - let native = true; - // Short-circuit native binary operator call if under Fast Operators mode if operator_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self @@ -257,8 +250,7 @@ impl Engine { return self .exec_fn_call( - None, global, caches, lib, name, native, *hashes, operands, false, false, pos, - level, + None, global, caches, lib, name, *hashes, operands, false, false, pos, level, ) .map(|(v, ..)| v); } @@ -266,7 +258,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] if !expr.namespace.is_empty() { // Qualified function call - let hash = hashes.native; + let hash = hashes.native(); let namespace = &expr.namespace; return self.make_qualified_function_call( @@ -287,7 +279,6 @@ impl Engine { lib, this_ptr, name, - native, first_arg, args, *hashes, diff --git a/src/func/call.rs b/src/func/call.rs index ef5d77e7..d840d7df 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,9 +11,9 @@ use crate::engine::{ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::Token; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, - ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, - Scope, ERR, + calc_fn_hash, calc_fn_params_hash, combine_hashes, is_valid_function_name, Dynamic, Engine, + FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, + RhaiResultOf, Scope, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -570,7 +570,6 @@ impl Engine { caches: &mut Caches, lib: &[&Module], fn_name: &str, - _native_only: bool, hashes: FnCallHashes, args: &mut FnCallArgs, is_ref_mut: bool, @@ -590,55 +589,58 @@ impl Engine { #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, args, is_ref_mut)?; - // These may be redirected from method style calls. - match fn_name { - // Handle type_of() - KEYWORD_TYPE_OF if args.len() == 1 => { - let typ = self.map_type_name(args[0].type_name()).to_string().into(); - return Ok((typ, false)); - } - - // Handle is_def_fn() - #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN - if args.len() == 2 && args[0].is::() && args[1].is::() => - { - let fn_name = args[0].read_lock::().expect("`FnPtr`"); - let num_params = args[1].as_int().expect("`INT`"); - - return Ok(( - if num_params < 0 || num_params > crate::MAX_USIZE_INT { - false - } else { - let hash_script = calc_fn_hash(None, fn_name.as_str(), num_params as usize); - self.has_script_fn(Some(global), caches, lib, hash_script) - } - .into(), - false, - )); - } - - // Handle is_shared() - #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => { - return no_method_err(fn_name, pos) - } - - KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if args.len() == 1 => { - return no_method_err(fn_name, pos) - } - - KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !args.is_empty() => { - return no_method_err(fn_name, pos) - } - - _ => (), - } - let level = level + 1; + // These may be redirected from method style calls. + if hashes.is_native_only() { + match fn_name { + // Handle type_of() + KEYWORD_TYPE_OF if args.len() == 1 => { + let typ = self.map_type_name(args[0].type_name()).to_string().into(); + return Ok((typ, false)); + } + + // Handle is_def_fn() + #[cfg(not(feature = "no_function"))] + crate::engine::KEYWORD_IS_DEF_FN + if args.len() == 2 && args[0].is::() && args[1].is::() => + { + let fn_name = args[0].read_lock::().expect("`FnPtr`"); + let num_params = args[1].as_int().expect("`INT`"); + + return Ok(( + if num_params < 0 || num_params > crate::MAX_USIZE_INT { + false + } else { + let hash_script = + calc_fn_hash(None, fn_name.as_str(), num_params as usize); + self.has_script_fn(Some(global), caches, lib, hash_script) + } + .into(), + false, + )); + } + + // Handle is_shared() + #[cfg(not(feature = "no_closure"))] + crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => { + return no_method_err(fn_name, pos) + } + + KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if args.len() == 1 => { + return no_method_err(fn_name, pos) + } + + KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !args.is_empty() => { + return no_method_err(fn_name, pos) + } + + _ => (), + } + } + #[cfg(not(feature = "no_function"))] - if !_native_only { + if !hashes.is_native_only() { // Script-defined function call? let local_entry = &mut None; @@ -649,7 +651,7 @@ impl Engine { local_entry, lib, fn_name, - hashes.script, + hashes.script(), None, false, None, @@ -719,7 +721,7 @@ impl Engine { } // Native function call - let hash = hashes.native; + let hash = hashes.native(); self.call_native_fn( global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, ) @@ -808,9 +810,13 @@ impl Engine { let fn_ptr = target.read_lock::().expect("`FnPtr`"); // Redirect function name let fn_name = fn_ptr.fn_name(); - let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hashes - let new_hash = calc_fn_hash(None, fn_name, args_len).into(); + let args_len = call_args.len() + fn_ptr.curry().len(); + let new_hash = if !fn_ptr.is_anonymous() && !is_valid_function_name(fn_name) { + FnCallHashes::from_native(calc_fn_hash(None, fn_name, args_len)) + } else { + calc_fn_hash(None, fn_name, args_len).into() + }; // Arguments are passed as-is, adding the curried arguments let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len()); curry.extend(fn_ptr.curry().iter().cloned()); @@ -825,7 +831,6 @@ impl Engine { caches, lib, fn_name, - false, new_hash, &mut args, false, @@ -845,20 +850,25 @@ impl Engine { // FnPtr call on object let fn_ptr = mem::take(&mut call_args[0]).cast::(); + let is_anon = fn_ptr.is_anonymous(); call_args = &mut call_args[1..]; // Redirect function name - let fn_name = fn_ptr.fn_name(); - let args_len = call_args.len() + fn_ptr.curry().len(); + let (fn_name, fn_curry) = fn_ptr.take_data(); // Recalculate hash - let new_hash = FnCallHashes::from_all( - #[cfg(not(feature = "no_function"))] - calc_fn_hash(None, fn_name, args_len), - calc_fn_hash(None, fn_name, args_len + 1), - ); + let args_len = call_args.len() + fn_curry.len(); + let new_hash = if !is_anon && !is_valid_function_name(&fn_name) { + FnCallHashes::from_native(calc_fn_hash(None, &fn_name, args_len + 1)) + } else { + FnCallHashes::from_all( + #[cfg(not(feature = "no_function"))] + calc_fn_hash(None, &fn_name, args_len), + calc_fn_hash(None, &fn_name, args_len + 1), + ) + }; // Replace the first argument with the object pointer, adding the curried arguments - let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len()); - curry.extend(fn_ptr.curry().iter().cloned()); + let mut curry = FnArgsVec::with_capacity(fn_curry.len()); + curry.extend(fn_curry.into_iter()); let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len() + 1); args.push(target.as_mut()); args.extend(curry.iter_mut()); @@ -870,8 +880,7 @@ impl Engine { global, caches, lib, - fn_name, - false, + &fn_name, new_hash, &mut args, is_ref_mut, @@ -939,11 +948,19 @@ impl Engine { call_args = &mut _arg_values; } // Recalculate the hash based on the new function name and new arguments - hash = FnCallHashes::from_all( - #[cfg(not(feature = "no_function"))] - calc_fn_hash(None, fn_name, call_args.len()), - calc_fn_hash(None, fn_name, call_args.len() + 1), - ); + hash = if !fn_ptr.is_anonymous() && !is_valid_function_name(&fn_name) { + FnCallHashes::from_native(calc_fn_hash( + None, + fn_name, + call_args.len() + 1, + )) + } else { + FnCallHashes::from_all( + #[cfg(not(feature = "no_function"))] + calc_fn_hash(None, fn_name, call_args.len()), + calc_fn_hash(None, fn_name, call_args.len() + 1), + ) + }; } } }; @@ -959,7 +976,6 @@ impl Engine { caches, lib, fn_name, - false, hash, &mut args, is_ref_mut, @@ -987,7 +1003,6 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, - native_only: bool, first_arg: Option<&Expr>, args_expr: &[Expr], hashes: FnCallHashes, @@ -996,7 +1011,6 @@ impl Engine { pos: Position, level: usize, ) -> RhaiResult { - let native = native_only; let mut first_arg = first_arg; let mut a_expr = args_expr; let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len(); @@ -1020,10 +1034,12 @@ impl Engine { } let fn_ptr = arg_value.cast::(); - curry.extend(fn_ptr.curry().iter().cloned()); + let is_anon = fn_ptr.is_anonymous(); + let (fn_name, fn_curry) = fn_ptr.take_data(); + curry.extend(fn_curry.into_iter()); // Redirect function name - redirected = fn_ptr.take_data().0; + redirected = fn_name; name = &redirected; // Shift the arguments @@ -1035,7 +1051,8 @@ impl Engine { // Recalculate hash let args_len = total_args + curry.len(); - hashes = if hashes.is_native_only() { + + hashes = if !is_anon && !is_valid_function_name(name) { FnCallHashes::from_native(calc_fn_hash(None, name, args_len)) } else { calc_fn_hash(None, name, args_len).into() @@ -1193,8 +1210,8 @@ impl Engine { return self .exec_fn_call( - scope, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, - pos, level, + scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, + level, ) .map(|(v, ..)| v); } @@ -1256,8 +1273,7 @@ impl Engine { } self.exec_fn_call( - None, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, pos, - level, + None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, ) .map(|(v, ..)| v) } diff --git a/src/func/native.rs b/src/func/native.rs index 6afda4da..a8eec976 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -416,7 +416,6 @@ impl<'a> NativeCallContext<'a> { caches, self.lib, fn_name, - false, hash, args, is_ref_mut, diff --git a/src/optimizer.rs b/src/optimizer.rs index 08abb8f2..01717a0a 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1142,7 +1142,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { + _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => { if let Some(result) = get_builtin_binary_op_fn(x.operator_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] diff --git a/src/parser.rs b/src/parser.rs index c2e02aa7..0eb767dd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -469,7 +469,7 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position // Variable name (Token::Identifier(s), pos) => Ok((*s, pos)), // Reserved keyword - (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { + (Token::Reserved(s), pos) if is_valid_identifier(s.as_str()) => { Err(PERR::Reserved(s.to_string()).into_err(pos)) } // Bad identifier @@ -486,7 +486,7 @@ fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> // Symbol (token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)), // Reserved symbol - (Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((*s, pos)), + (Token::Reserved(s), pos) if !is_valid_identifier(s.as_str()) => Ok((*s, pos)), // Bad symbol (Token::LexError(err), pos) => Err(err.into_err(pos)), // Not a symbol @@ -599,9 +599,7 @@ impl Engine { #[cfg(feature = "no_module")] let hash = calc_fn_hash(None, &id, 0); - let is_valid_function_name = is_valid_function_name(&id); - - let hashes = if is_valid_function_name { + let hashes = if is_valid_function_name(&id) { hash.into() } else { FnCallHashes::from_native(hash) @@ -613,8 +611,6 @@ impl Engine { name: state.get_interned_string(id), capture_parent_scope, operator_token: None, - #[cfg(not(feature = "no_function"))] - can_be_script: is_valid_function_name, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -671,9 +667,7 @@ impl Engine { #[cfg(feature = "no_module")] let hash = calc_fn_hash(None, &id, args.len()); - let is_valid_function_name = is_valid_function_name(&id); - - let hashes = if is_valid_function_name { + let hashes = if is_valid_function_name(&id) { hash.into() } else { FnCallHashes::from_native(hash) @@ -685,8 +679,6 @@ impl Engine { name: state.get_interned_string(id), capture_parent_scope, operator_token: None, - #[cfg(not(feature = "no_function"))] - can_be_script: is_valid_function_name, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -1012,7 +1004,7 @@ impl Engine { (Token::InterpolatedString(..), pos) => { return Err(PERR::PropertyExpected.into_err(pos)) } - (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { + (Token::Reserved(s), pos) if is_valid_identifier(s.as_str()) => { return Err(PERR::Reserved(s.to_string()).into_err(pos)); } (Token::LexError(err), pos) => return Err(err.into_err(pos)), @@ -1944,8 +1936,6 @@ impl Engine { pos, operator_token: Some(token), capture_parent_scope: false, - #[cfg(not(feature = "no_function"))] - can_be_script: false, } .into_fn_call_expr(pos)) } @@ -1976,8 +1966,6 @@ impl Engine { pos, operator_token: Some(token), capture_parent_scope: false, - #[cfg(not(feature = "no_function"))] - can_be_script: false, } .into_fn_call_expr(pos)) } @@ -2001,8 +1989,6 @@ impl Engine { pos, operator_token: Some(token), capture_parent_scope: false, - #[cfg(not(feature = "no_function"))] - can_be_script: false, } .into_fn_call_expr(pos)) } @@ -2217,11 +2203,15 @@ impl Engine { // lhs.func(...) (lhs, Expr::FnCall(mut func, func_pos)) => { // Recalculate hash - func.hashes = FnCallHashes::from_all( - #[cfg(not(feature = "no_function"))] - calc_fn_hash(None, &func.name, func.args.len()), - calc_fn_hash(None, &func.name, func.args.len() + 1), - ); + func.hashes = if is_valid_function_name(&func.name) { + FnCallHashes::from_all( + #[cfg(not(feature = "no_function"))] + calc_fn_hash(None, &func.name, func.args.len()), + calc_fn_hash(None, &func.name, func.args.len() + 1), + ) + } else { + FnCallHashes::from_native(calc_fn_hash(None, &func.name, func.args.len() + 1)) + }; let rhs = Expr::MethodCall(func, func_pos); Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_flags, op_pos)) @@ -2263,11 +2253,19 @@ impl Engine { // lhs.func().dot_rhs or lhs.func()[idx_rhs] Expr::FnCall(mut func, func_pos) => { // Recalculate hash - func.hashes = FnCallHashes::from_all( - #[cfg(not(feature = "no_function"))] - calc_fn_hash(None, &func.name, func.args.len()), - calc_fn_hash(None, &func.name, func.args.len() + 1), - ); + func.hashes = if is_valid_function_name(&func.name) { + FnCallHashes::from_all( + #[cfg(not(feature = "no_function"))] + calc_fn_hash(None, &func.name, func.args.len()), + calc_fn_hash(None, &func.name, func.args.len() + 1), + ) + } else { + FnCallHashes::from_native(calc_fn_hash( + None, + &func.name, + func.args.len() + 1, + )) + }; let new_lhs = BinaryExpr { lhs: Expr::MethodCall(func, func_pos), @@ -2322,7 +2320,7 @@ impl Engine { .get(&**c) .copied() .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?, - Token::Reserved(c) if !is_valid_identifier(c.chars()) => { + Token::Reserved(c) if !is_valid_identifier(c) => { return Err(PERR::UnknownOperator(c.to_string()).into_err(*current_pos)) } _ => current_op.precedence(), @@ -2347,7 +2345,7 @@ impl Engine { .get(&**c) .copied() .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?, - Token::Reserved(c) if !is_valid_identifier(c.chars()) => { + Token::Reserved(c) if !is_valid_identifier(c) => { return Err(PERR::UnknownOperator(c.to_string()).into_err(*next_pos)) } _ => next_op.precedence(), @@ -2371,8 +2369,8 @@ impl Engine { let op = op_token.syntax(); let hash = calc_fn_hash(None, &op, 2); - let is_function = is_valid_function_name(&op); - let operator_token = if is_function { + let is_valid_script_function = is_valid_function_name(&op); + let operator_token = if is_valid_script_function { None } else { Some(op_token.clone()) @@ -2387,8 +2385,6 @@ impl Engine { pos, operator_token, capture_parent_scope: false, - #[cfg(not(feature = "no_function"))] - can_be_script: is_function, }; let mut args = StaticVec::new_const(); @@ -2474,7 +2470,7 @@ impl Engine { let pos = args[0].start_position(); FnCallExpr { - hashes: if is_function { + hashes: if is_valid_script_function { hash.into() } else { FnCallHashes::from_native(hash) @@ -3732,8 +3728,6 @@ impl Engine { pos, operator_token: None, capture_parent_scope: false, - #[cfg(not(feature = "no_function"))] - can_be_script: false, } .into_fn_call_expr(pos); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6aa0ec43..96a56170 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2199,7 +2199,7 @@ fn get_identifier( } } - let is_valid_identifier = is_valid_identifier(identifier.chars()); + let is_valid_identifier = is_valid_identifier(&identifier); if let Some(token) = Token::lookup_from_syntax(&identifier) { return (token, start_pos); @@ -2233,10 +2233,10 @@ pub fn is_keyword_function(name: &str) -> bool { /// _(internals)_ Is a text string a valid identifier? /// Exported under the `internals` feature only. #[must_use] -pub fn is_valid_identifier(name: impl Iterator) -> bool { +pub fn is_valid_identifier(name: &str) -> bool { let mut first_alphabetic = false; - for ch in name { + for ch in name.chars() { match ch { '_' => (), _ if is_id_first_alphabetic(ch) => first_alphabetic = true, @@ -2254,7 +2254,7 @@ pub fn is_valid_identifier(name: impl Iterator) -> bool { #[inline(always)] #[must_use] pub fn is_valid_function_name(name: &str) -> bool { - is_valid_identifier(name.chars()) + is_valid_identifier(name) && !is_keyword_function(name) } /// Is a character valid to start an identifier? @@ -2433,7 +2433,7 @@ impl<'a> Iterator for TokenIterator<'a> { (.., true) => unreachable!("no custom operators"), // Reserved keyword that is not custom and disabled. (token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => { - let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}); + let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"}); Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into()) }, // Reserved keyword/operator that is not custom. diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 8c5d4b1f..1bdc8652 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -1,10 +1,9 @@ //! The `FnPtr` type. -use crate::tokenizer::is_valid_identifier; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError, - RhaiResult, RhaiResultOf, StaticVec, AST, ERR, + is_valid_function_name, Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, + Position, RhaiError, RhaiResult, RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -254,7 +253,7 @@ impl TryFrom for FnPtr { #[inline(always)] fn try_from(value: ImmutableString) -> RhaiResultOf { - if is_valid_identifier(value.chars()) { + if is_valid_function_name(&value) { Ok(Self { name: value, curry: StaticVec::new_const(), diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index d11a49c6..86fedfdf 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -238,7 +238,7 @@ impl fmt::Display for ParseErrorType { Self::AssignmentToInvalidLHS(s) => f.write_str(s), Self::LiteralTooLarge(typ, max) => write!(f, "{typ} exceeds the maximum limit ({max})"), - Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{s}' is a reserved keyword"), + Self::Reserved(s) if is_valid_identifier(s.as_str()) => write!(f, "'{s}' is a reserved keyword"), Self::Reserved(s) => write!(f, "'{s}' is a reserved symbol"), Self::UnexpectedEOF => f.write_str("Script is incomplete"), Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"), From 8b773aa15ecd3c7081bfade5c43c1b824a0c4ebb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 30 Oct 2022 16:13:51 +0800 Subject: [PATCH 077/116] Fix builds. --- src/eval/global_state.rs | 1 + src/func/call.rs | 33 +++++++++++++++++++++++++++------ src/types/fn_ptr.rs | 5 +++-- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index bf4489f9..db4c5332 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -286,6 +286,7 @@ impl GlobalRuntimeState<'_> { /// Get the current source. #[inline(always)] #[must_use] + #[allow(dead_code)] pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> { self.source.as_ref() } diff --git a/src/func/call.rs b/src/func/call.rs index d840d7df..387fd694 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -9,11 +9,11 @@ use crate::engine::{ KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; -use crate::tokenizer::Token; +use crate::tokenizer::{is_valid_function_name, Token}; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, is_valid_function_name, Dynamic, Engine, - FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, - RhaiResultOf, Scope, ERR, + calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, + ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, + Scope, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -808,11 +808,17 @@ impl Engine { KEYWORD_FN_PTR_CALL if target.is::() => { // FnPtr call let fn_ptr = target.read_lock::().expect("`FnPtr`"); + + #[cfg(not(feature = "no_function"))] + let is_anon = fn_ptr.is_anonymous(); + #[cfg(feature = "no_function")] + let is_anon = false; + // Redirect function name let fn_name = fn_ptr.fn_name(); // Recalculate hashes let args_len = call_args.len() + fn_ptr.curry().len(); - let new_hash = if !fn_ptr.is_anonymous() && !is_valid_function_name(fn_name) { + let new_hash = if !is_anon && !is_valid_function_name(fn_name) { FnCallHashes::from_native(calc_fn_hash(None, fn_name, args_len)) } else { calc_fn_hash(None, fn_name, args_len).into() @@ -850,7 +856,12 @@ impl Engine { // FnPtr call on object let fn_ptr = mem::take(&mut call_args[0]).cast::(); + + #[cfg(not(feature = "no_function"))] let is_anon = fn_ptr.is_anonymous(); + #[cfg(feature = "no_function")] + let is_anon = false; + call_args = &mut call_args[1..]; // Redirect function name @@ -934,6 +945,11 @@ impl Engine { if let Some(map) = target.read_lock::() { if let Some(val) = map.get(fn_name) { if let Some(fn_ptr) = val.read_lock::() { + #[cfg(not(feature = "no_function"))] + let is_anon = fn_ptr.is_anonymous(); + #[cfg(feature = "no_function")] + let is_anon = false; + // Remap the function name _redirected = fn_ptr.fn_name_raw().clone(); fn_name = &_redirected; @@ -948,7 +964,7 @@ impl Engine { call_args = &mut _arg_values; } // Recalculate the hash based on the new function name and new arguments - hash = if !fn_ptr.is_anonymous() && !is_valid_function_name(&fn_name) { + hash = if !is_anon && !is_valid_function_name(&fn_name) { FnCallHashes::from_native(calc_fn_hash( None, fn_name, @@ -1034,7 +1050,12 @@ impl Engine { } let fn_ptr = arg_value.cast::(); + + #[cfg(not(feature = "no_function"))] let is_anon = fn_ptr.is_anonymous(); + #[cfg(feature = "no_function")] + let is_anon = false; + let (fn_name, fn_curry) = fn_ptr.take_data(); curry.extend(fn_curry.into_iter()); diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 1bdc8652..2c2bd7d2 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -1,9 +1,10 @@ //! The `FnPtr` type. +use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ - is_valid_function_name, Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, - Position, RhaiError, RhaiResult, RhaiResultOf, StaticVec, AST, ERR, + Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError, + RhaiResult, RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; From 22ee12531c1ffead392883ca6eaa8ab95277de2c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 30 Oct 2022 18:43:18 +0800 Subject: [PATCH 078/116] Code cleanup. --- src/api/call_fn.rs | 2 +- src/ast/expr.rs | 88 +++++++++++++------------ src/ast/flags.rs | 4 +- src/eval/chaining.rs | 14 ++-- src/eval/debugger.rs | 6 +- src/eval/expr.rs | 90 +------------------------ src/eval/stmt.rs | 32 +++++++-- src/func/call.rs | 152 +++++++++++++++++++++++++++++-------------- src/func/mod.rs | 3 +- src/func/native.rs | 2 +- src/optimizer.rs | 10 +-- src/parser.rs | 129 +++++++++++++----------------------- 12 files changed, 246 insertions(+), 286 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 3b0a7615..8c0139e8 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -274,7 +274,7 @@ impl Engine { // Check for data race. #[cfg(not(feature = "no_closure"))] - crate::func::call::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?; + crate::func::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?; if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) { self.call_script_fn( diff --git a/src/ast/expr.rs b/src/ast/expr.rs index f42209b6..52205808 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -17,7 +17,7 @@ use std::{ fmt::Write, hash::Hash, iter::once, - num::{NonZeroU8, NonZeroUsize}, + num::{NonZeroU64, NonZeroU8, NonZeroUsize}, }; #[cfg(not(feature = "no_float"))] @@ -86,7 +86,7 @@ impl CustomExpr { /// /// Two separate hashes are pre-calculated because of the following patterns: /// -/// ```js +/// ```rhai /// func(a, b, c); // Native: func(a, b, c) - 3 parameters /// // Script: func(a, b, c) - 3 parameters /// @@ -100,22 +100,22 @@ impl CustomExpr { /// /// Function call hashes are used in the following manner: /// -/// * First, the script hash is tried, which contains only the called function's name plus the -/// number of parameters. +/// * First, the script hash (if any) is tried, which contains only the called function's name plus +/// the number of parameters. /// /// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is -/// then used to search for a native function. In other words, a complete native function call -/// hash always contains the called function's name plus the types of the arguments. This is due -/// to possible function overloading for different parameter types. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] +/// then used to search for a native function. +/// +/// In other words, a complete native function call hash always contains the called function's +/// name plus the types of the arguments. This is due to possible function overloading for +/// different parameter types. +#[derive(Clone, Copy, Eq, PartialEq, Hash)] pub struct FnCallHashes { - /// Pre-calculated hash for a script-defined function (zero if native functions only). + /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). #[cfg(not(feature = "no_function"))] - script: u64, + script: Option, /// Pre-calculated hash for a native Rust function with no parameter types. - /// - /// This hash can never be zero. - native: u64, + native: NonZeroU64, } impl fmt::Debug for FnCallHashes { @@ -123,11 +123,11 @@ impl fmt::Debug for FnCallHashes { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_function"))] - if self.script != 0 { - return if self.script == self.native { + if let Some(script) = self.script { + return if script == self.native { fmt::Debug::fmt(&self.native, f) } else { - write!(f, "({}, {})", self.script, self.native) + write!(f, "({}, {})", script, self.native) }; } @@ -138,11 +138,11 @@ impl fmt::Debug for FnCallHashes { impl From for FnCallHashes { #[inline] fn from(hash: u64) -> Self { - let hash = if hash == 0 { ALT_ZERO_HASH } else { hash }; + let hash = NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap(); Self { #[cfg(not(feature = "no_function"))] - script: hash, + script: Some(hash), native: hash, } } @@ -150,23 +150,23 @@ impl From for FnCallHashes { impl FnCallHashes { /// Create a [`FnCallHashes`] with only the native Rust hash. - #[inline(always)] + #[inline] #[must_use] - pub const fn from_native(hash: u64) -> Self { + pub fn from_native(hash: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: 0, - native: if hash == 0 { ALT_ZERO_HASH } else { hash }, + script: None, + native: NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap(), } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. - #[inline(always)] + #[inline] #[must_use] - pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { + pub fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: if script == 0 { ALT_ZERO_HASH } else { script }, - native: if native == 0 { ALT_ZERO_HASH } else { native }, + script: NonZeroU64::new(if script == 0 { ALT_ZERO_HASH } else { script }), + native: NonZeroU64::new(if native == 0 { ALT_ZERO_HASH } else { native }).unwrap(), } } /// Is this [`FnCallHashes`] native-only? @@ -174,23 +174,31 @@ impl FnCallHashes { #[must_use] pub const fn is_native_only(&self) -> bool { #[cfg(not(feature = "no_function"))] - return self.script == 0; + return self.script.is_none(); #[cfg(feature = "no_function")] return true; } /// Get the native hash. + /// + /// The hash returned is never zero. #[inline(always)] #[must_use] - pub const fn native(&self) -> u64 { - self.native + pub fn native(&self) -> u64 { + self.native.get() } /// Get the script hash. + /// + /// The hash returned is never zero. + /// + /// # Panics + /// + /// Panics if this [`FnCallHashes`] is native-only. #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub const fn script(&self) -> u64 { - assert!(self.script != 0); - self.script + pub fn script(&self) -> u64 { + assert!(self.script.is_some()); + self.script.as_ref().unwrap().get() } } @@ -210,9 +218,7 @@ pub struct FnCallExpr { /// Does this function call capture the parent scope? pub capture_parent_scope: bool, /// Is this function call a native operator? - pub operator_token: Option, - /// [Position] of the function name. - pub pos: Position, + pub op_token: Option, } impl fmt::Debug for FnCallExpr { @@ -227,13 +233,12 @@ impl fmt::Debug for FnCallExpr { ff.field("hash", &self.hashes) .field("name", &self.name) .field("args", &self.args); - if let Some(ref token) = self.operator_token { - ff.field("operator_token", token); + if let Some(ref token) = self.op_token { + ff.field("op_token", token); } if self.capture_parent_scope { ff.field("capture_parent_scope", &self.capture_parent_scope); } - ff.field("pos", &self.pos); ff.finish() } } @@ -698,8 +703,7 @@ impl Expr { hashes: calc_fn_hash(None, f.fn_name(), 1).into(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, - operator_token: None, - pos, + op_token: None, } .into(), pos, @@ -754,6 +758,8 @@ impl Expr { | Self::And(.., pos) | Self::Or(.., pos) | Self::Coalesce(.., pos) + | Self::FnCall(.., pos) + | Self::MethodCall(.., pos) | Self::Index(.., pos) | Self::Dot(.., pos) | Self::InterpolatedString(.., pos) @@ -762,8 +768,6 @@ impl Expr { #[cfg(not(feature = "no_custom_syntax"))] Self::Custom(.., pos) => *pos, - Self::FnCall(x, ..) | Self::MethodCall(x, ..) => x.pos, - Self::Stmt(x) => x.position(), } } diff --git a/src/ast/flags.rs b/src/ast/flags.rs index e26fb55c..452a57e6 100644 --- a/src/ast/flags.rs +++ b/src/ast/flags.rs @@ -18,7 +18,7 @@ pub enum FnAccess { impl FnAccess { /// Is this function private? - #[inline] + #[inline(always)] #[must_use] pub const fn is_private(self) -> bool { match self { @@ -27,7 +27,7 @@ impl FnAccess { } } /// Is this function public? - #[inline] + #[inline(always)] #[must_use] pub const fn is_public(self) -> bool { match self { diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index dbf40e3a..07c1f676 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -269,7 +269,7 @@ impl Engine { if op_info.is_op_assignment() { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self - .call_native_fn( + .exec_native_fn_call( global, caches, lib, getter, *hash_get, args, is_ref_mut, false, *pos, level, ) @@ -303,7 +303,7 @@ impl Engine { } let args = &mut [target.as_mut(), &mut new_val]; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, setter, *hash_set, args, is_ref_mut, false, *pos, level, ) @@ -330,7 +330,7 @@ impl Engine { let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, getter, *hash_get, args, is_ref_mut, false, *pos, level, ) @@ -429,7 +429,7 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self - .call_native_fn( + .exec_native_fn_call( global, caches, lib, getter, *hash_get, args, is_ref_mut, false, pos, level, ) @@ -465,7 +465,7 @@ impl Engine { // Re-use args because the first &mut parameter will not be consumed let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, setter, *hash_set, args, is_ref_mut, false, pos, level, ) @@ -764,7 +764,7 @@ impl Engine { let pos = Position::NONE; let level = level + 1; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, fn_name, hash, args, true, false, pos, level, ) .map(|(r, ..)| r) @@ -789,7 +789,7 @@ impl Engine { let pos = Position::NONE; let level = level + 1; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, ) } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index f87e4a81..2ddf0991 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -430,7 +430,7 @@ impl Engine { } /// Run the debugger callback if there is a debugging interface registered. /// - /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or /// function call. /// /// It is up to the [`Engine`] to reactivate the debugger. @@ -452,7 +452,7 @@ impl Engine { } /// Run the debugger callback. /// - /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or /// function call. /// /// It is up to the [`Engine`] to reactivate the debugger. @@ -498,7 +498,7 @@ impl Engine { } /// Run the debugger callback unconditionally. /// - /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or /// function call. /// /// It is up to the [`Engine`] to reactivate the debugger. diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 17c2d82d..521cb036 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -1,9 +1,8 @@ //! Module defining functions for evaluating an expression. use super::{Caches, EvalContext, GlobalRuntimeState, Target}; -use crate::ast::{Expr, FnCallExpr, OpAssignment}; +use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; -use crate::func::get_builtin_binary_op_fn; use crate::types::dynamic::AccessMode; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; use std::num::NonZeroUsize; @@ -206,89 +205,6 @@ impl Engine { Ok((val.into(), var_pos)) } - /// Evaluate a function call expression. - pub(crate) fn eval_fn_call_expr( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - caches: &mut Caches, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &FnCallExpr, - pos: Position, - level: usize, - ) -> RhaiResult { - let FnCallExpr { - name, - hashes, - args, - operator_token, - .. - } = expr; - - // Short-circuit native binary operator call if under Fast Operators mode - if operator_token.is_some() && self.fast_operators() && args.len() == 2 { - let mut lhs = self - .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? - .0 - .flatten(); - - let mut rhs = self - .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? - .0 - .flatten(); - - let operands = &mut [&mut lhs, &mut rhs]; - - if let Some(func) = - get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1]) - { - // Built-in found - let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); - return func(context, operands); - } - - return self - .exec_fn_call( - None, global, caches, lib, name, *hashes, operands, false, false, pos, level, - ) - .map(|(v, ..)| v); - } - - #[cfg(not(feature = "no_module"))] - if !expr.namespace.is_empty() { - // Qualified function call - let hash = hashes.native(); - let namespace = &expr.namespace; - - return self.make_qualified_function_call( - scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, - ); - } - - // Normal function call - let (first_arg, args) = args.split_first().map_or_else( - || (None, args.as_ref()), - |(first, rest)| (Some(first), rest), - ); - - self.make_function_call( - scope, - global, - caches, - lib, - this_ptr, - name, - first_arg, - args, - *hashes, - expr.capture_parent_scope, - expr.operator_token.as_ref(), - pos, - level, - ) - } - /// Evaluate an expression. // // # Implementation Notes @@ -312,7 +228,7 @@ impl Engine { // Function calls should account for a relatively larger portion of expressions because // binary operators are also function calls. - if let Expr::FnCall(x, ..) = expr { + if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?; @@ -320,7 +236,7 @@ impl Engine { self.track_operation(global, expr.position())?; let result = - self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); + self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, *pos, level); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 0a302ee6..835b7b91 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -153,7 +153,7 @@ impl Engine { let op_assign = op_assign.literal_syntax(); let op = op.literal_syntax(); - match self.call_native_fn( + match self.exec_native_fn_call( global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { Ok(_) => (), @@ -161,7 +161,7 @@ impl Engine { { // Expand to `var = var op rhs` *args[0] = self - .call_native_fn( + .exec_native_fn_call( global, caches, lib, op, *hash_op, args, true, false, *op_pos, level, ) .map_err(|err| err.fill_position(op_info.pos))? @@ -207,11 +207,11 @@ impl Engine { // Popular branches are lifted out of the `match` statement into their own branches. // Function calls should account for a relatively larger portion of statements. - if let Stmt::FnCall(x, ..) = stmt { + if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; let result = - self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); + self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, *pos, level); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -1006,4 +1006,28 @@ impl Engine { result } + + /// Evaluate a list of statements with no `this` pointer. + /// This is commonly used to evaluate a list of statements in an [`AST`][crate::AST] or a script function body. + #[inline] + pub(crate) fn eval_global_statements( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + caches: &mut Caches, + statements: &[Stmt], + lib: &[&Module], + level: usize, + ) -> RhaiResult { + self.eval_stmt_block( + scope, global, caches, lib, &mut None, statements, false, level, + ) + .or_else(|err| match *err { + ERR::Return(out, ..) => Ok(out), + ERR::LoopBreak(..) => { + unreachable!("no outer loop scope to break out of") + } + _ => Err(err), + }) + } } diff --git a/src/func/call.rs b/src/func/call.rs index 387fd694..b757dda3 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1,9 +1,8 @@ //! Implement function-calling mechanism for [`Engine`]. -use super::callable_function::CallableFunction; -use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; +use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn, CallableFunction}; use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; -use crate::ast::{Expr, FnCallHashes, Stmt}; +use crate::ast::{Expr, FnCallExpr, FnCallHashes}; use crate::engine::{ KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, @@ -108,17 +107,14 @@ impl Drop for ArgBackup<'_> { } } +// Ensure no data races in function call arguments. #[cfg(not(feature = "no_closure"))] #[inline] -pub fn ensure_no_data_race( - fn_name: &str, - args: &FnCallArgs, - is_method_call: bool, -) -> RhaiResultOf<()> { +pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) -> RhaiResultOf<()> { if let Some((n, ..)) = args .iter() .enumerate() - .skip(if is_method_call { 1 } else { 0 }) + .skip(if is_ref_mut { 1 } else { 0 }) .find(|(.., a)| a.is_locked()) { return Err(ERR::ErrorDataRace( @@ -134,7 +130,7 @@ pub fn ensure_no_data_race( /// Generate the signature for a function call. #[inline] #[must_use] -pub fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynamic]) -> String { +fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynamic]) -> String { format!( "{fn_name} ({})", args.iter() @@ -154,7 +150,7 @@ pub fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynami #[cfg(not(feature = "no_module"))] #[inline] #[must_use] -pub fn gen_qualified_fn_call_signature( +fn gen_qualified_fn_call_signature( engine: &Engine, namespace: &crate::ast::Namespace, fn_name: &str, @@ -343,7 +339,7 @@ impl Engine { /// /// **DO NOT** reuse the argument values unless for the first `&mut` argument - /// all others are silently replaced by `()`! - pub(crate) fn call_native_fn( + pub(crate) fn exec_native_fn_call( &self, global: &mut GlobalRuntimeState, caches: &mut Caches, @@ -716,41 +712,17 @@ impl Engine { // Restore the original source global.source = orig_source; - return Ok((result?, false)); + return result.map(|r| (r, false)); } } // Native function call let hash = hashes.native(); - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, ) } - /// Evaluate a list of statements with no `this` pointer. - /// This is commonly used to evaluate a list of statements in an [`AST`][crate::AST] or a script function body. - #[inline] - pub(crate) fn eval_global_statements( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - caches: &mut Caches, - statements: &[Stmt], - lib: &[&Module], - level: usize, - ) -> RhaiResult { - self.eval_stmt_block( - scope, global, caches, lib, &mut None, statements, false, level, - ) - .or_else(|err| match *err { - ERR::Return(out, ..) => Ok(out), - ERR::LoopBreak(..) => { - unreachable!("no outer loop scope to break out of") - } - _ => Err(err), - }) - } - /// Evaluate an argument. #[inline] pub(crate) fn get_arg_value( @@ -763,13 +735,14 @@ impl Engine { arg_expr: &Expr, level: usize, ) -> RhaiResultOf<(Dynamic, Position)> { - #[cfg(feature = "debugging")] - if self.debugger.is_some() { - if let Some(value) = arg_expr.get_literal_value() { - #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, arg_expr, level)?; - return Ok((value, arg_expr.start_position())); - } + // Literal values + if let Some(value) = arg_expr.get_literal_value() { + self.track_operation(global, arg_expr.start_position())?; + + #[cfg(feature = "debugging")] + self.run_debugger(scope, global, lib, this_ptr, arg_expr, level)?; + + return Ok((value, arg_expr.start_position())); } // Do not match function exit for arguments @@ -784,7 +757,7 @@ impl Engine { #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); - Ok((result?, arg_expr.start_position())) + result.map(|r| (r, arg_expr.start_position())) } /// Call a dot method. @@ -1022,8 +995,8 @@ impl Engine { first_arg: Option<&Expr>, args_expr: &[Expr], hashes: FnCallHashes, + is_operator_call: bool, capture_scope: bool, - operator_token: Option<&Token>, pos: Position, level: usize, ) -> RhaiResult { @@ -1036,7 +1009,7 @@ impl Engine { let redirected; // Handle call() - Redirect function call match name { - _ if operator_token.is_some() => (), + _ if is_operator_call => (), // Handle call() KEYWORD_FN_PTR_CALL if total_args >= 1 => { @@ -1520,4 +1493,87 @@ impl Engine { // Evaluate the AST self.eval_global_statements(scope, global, caches, statements, lib, level) } + + /// Evaluate a function call expression. + pub(crate) fn eval_fn_call_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + caches: &mut Caches, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &FnCallExpr, + pos: Position, + level: usize, + ) -> RhaiResult { + let FnCallExpr { + name, + hashes, + args, + op_token, + .. + } = expr; + + // Short-circuit native binary operator call if under Fast Operators mode + if op_token.is_some() && self.fast_operators() && args.len() == 2 { + let mut lhs = self + .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? + .0 + .flatten(); + + let mut rhs = self + .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? + .0 + .flatten(); + + let operands = &mut [&mut lhs, &mut rhs]; + + if let Some(func) = + get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1]) + { + // Built-in found + let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); + return func(context, operands); + } + + return self + .exec_fn_call( + None, global, caches, lib, name, *hashes, operands, false, false, pos, level, + ) + .map(|(v, ..)| v); + } + + #[cfg(not(feature = "no_module"))] + if !expr.namespace.is_empty() { + // Qualified function call + let hash = hashes.native(); + let namespace = &expr.namespace; + + return self.make_qualified_function_call( + scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, + ); + } + + // Normal function call + let (first_arg, args) = args.split_first().map_or_else( + || (None, args.as_ref()), + |(first, rest)| (Some(first), rest), + ); + + self.make_function_call( + scope, + global, + caches, + lib, + this_ptr, + name, + first_arg, + args, + *hashes, + expr.op_token.is_some(), + expr.capture_parent_scope, + pos, + level, + ) + } } diff --git a/src/func/mod.rs b/src/func/mod.rs index 8298a63e..42ccb91d 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -14,8 +14,7 @@ pub mod script; pub use args::FuncArgs; pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; #[cfg(not(feature = "no_module"))] -pub use call::gen_qualified_fn_call_signature; -pub use call::{gen_fn_call_signature, FnCallArgs}; +pub use call::{ensure_no_data_race, FnCallArgs}; pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; diff --git a/src/func/native.rs b/src/func/native.rs index a8eec976..8378b075 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -382,7 +382,7 @@ impl<'a> NativeCallContext<'a> { if native_only { return self .engine() - .call_native_fn( + .exec_native_fn_call( global, caches, self.lib, diff --git a/src/optimizer.rs b/src/optimizer.rs index 01717a0a..f0424ed0 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -147,7 +147,7 @@ impl<'a> OptimizerState<'a> { let lib = &[]; self.engine - .call_native_fn( + .exec_native_fn_call( &mut self.global, &mut self.caches, lib, @@ -444,9 +444,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b ) => { match x.1.rhs { - Expr::FnCall(ref mut x2, ..) => { + Expr::FnCall(ref mut x2, pos) => { state.set_dirty(); - x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, x2.pos); + x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, pos); x.1.rhs = mem::take(&mut x2.args[1]); } ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr), @@ -1142,8 +1142,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => { - if let Some(result) = get_builtin_binary_op_fn(x.operator_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) + _ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => { + if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] let lib = state.lib; diff --git a/src/parser.rs b/src/parser.rs index 0eb767dd..31391363 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -174,7 +174,7 @@ impl<'e> ParseState<'e> { /// /// # Return value: `(index, is_func_name)` /// - /// * `index`: `None` when the variable name is not found in the `stack`, + /// * `index`: [`None`] when the variable name is not found in the `stack`, /// otherwise the index value. /// /// * `is_func_name`: `true` if the variable is actually the name of a function @@ -223,7 +223,7 @@ impl<'e> ParseState<'e> { /// Returns the offset to be deducted from `Stack::len`, /// i.e. the top element of the [`ParseState`] is offset 1. /// - /// Returns `None` when the variable name is not found in the [`ParseState`]. + /// Returns [`None`] when the variable name is not found in the [`ParseState`]. /// /// # Panics /// @@ -610,12 +610,11 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - operator_token: None, + op_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, args, - pos: settings.pos, } .into_fn_call_expr(settings.pos)); } @@ -678,12 +677,11 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - operator_token: None, + op_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, args, - pos: settings.pos, } .into_fn_call_expr(settings.pos)); } @@ -1933,8 +1931,7 @@ impl Engine { name: state.get_interned_string("-"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), args, - pos, - operator_token: Some(token), + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1963,8 +1960,7 @@ impl Engine { name: state.get_interned_string("+"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), args, - pos, - operator_token: Some(token), + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1986,8 +1982,7 @@ impl Engine { name: state.get_interned_string("!"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), args, - pos, - operator_token: Some(token), + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -2376,25 +2371,24 @@ impl Engine { Some(op_token.clone()) }; - let op_base = FnCallExpr { - #[cfg(not(feature = "no_module"))] - namespace: Default::default(), - name: state.get_interned_string(op.as_ref()), - hashes: FnCallHashes::from_native(hash), - args: StaticVec::new_const(), - pos, - operator_token, - capture_parent_scope: false, - }; - let mut args = StaticVec::new_const(); args.push(root); args.push(rhs); args.shrink_to_fit(); + let mut op_base = FnCallExpr { + #[cfg(not(feature = "no_module"))] + namespace: Default::default(), + name: state.get_interned_string(op.as_ref()), + hashes: FnCallHashes::from_native(hash), + args, + op_token: operator_token, + capture_parent_scope: false, + }; + root = match op_token { // '!=' defaults to true when passed invalid operands - Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), + Token::NotEqualsTo => op_base.into_fn_call_expr(pos), // Comparison operators default to false when passed invalid operands Token::EqualsTo @@ -2402,61 +2396,36 @@ impl Engine { | Token::LessThanEqualsTo | Token::GreaterThan | Token::GreaterThanEqualsTo => { - let pos = args[0].start_position(); - FnCallExpr { args, ..op_base }.into_fn_call_expr(pos) + let pos = op_base.args[0].start_position(); + op_base.into_fn_call_expr(pos) } Token::Or => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); - Expr::Or( - BinaryExpr { - lhs: current_lhs.ensure_bool_expr()?, - rhs: rhs.ensure_bool_expr()?, - } - .into(), - pos, - ) + let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + Expr::Or(BinaryExpr { lhs: lhs, rhs: rhs }.into(), pos) } Token::And => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); - Expr::And( - BinaryExpr { - lhs: current_lhs.ensure_bool_expr()?, - rhs: rhs.ensure_bool_expr()?, - } - .into(), - pos, - ) + let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + Expr::And(BinaryExpr { lhs: lhs, rhs: rhs }.into(), pos) } Token::DoubleQuestion => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); - Expr::Coalesce( - BinaryExpr { - lhs: current_lhs, - rhs, - } - .into(), - pos, - ) + let rhs = op_base.args.pop().unwrap(); + let lhs = op_base.args.pop().unwrap(); + Expr::Coalesce(BinaryExpr { lhs, rhs }.into(), pos) } Token::In => { // Swap the arguments - let current_lhs = args.remove(0); - let pos = current_lhs.start_position(); - args.push(current_lhs); - args.shrink_to_fit(); + let lhs = op_base.args.remove(0); + let pos = lhs.start_position(); + op_base.args.push(lhs); + op_base.args.shrink_to_fit(); // Convert into a call to `contains` - FnCallExpr { - hashes: calc_fn_hash(None, OP_CONTAINS, 2).into(), - args, - name: state.get_interned_string(OP_CONTAINS), - ..op_base - } - .into_fn_call_expr(pos) + op_base.hashes = calc_fn_hash(None, OP_CONTAINS, 2).into(); + op_base.name = state.get_interned_string(OP_CONTAINS); + op_base.into_fn_call_expr(pos) } #[cfg(not(feature = "no_custom_syntax"))] @@ -2466,24 +2435,17 @@ impl Engine { .get(s.as_str()) .map_or(false, Option::is_some) => { - let hash = calc_fn_hash(None, &s, 2); - let pos = args[0].start_position(); - - FnCallExpr { - hashes: if is_valid_script_function { - hash.into() - } else { - FnCallHashes::from_native(hash) - }, - args, - ..op_base - } - .into_fn_call_expr(pos) + op_base.hashes = if is_valid_script_function { + calc_fn_hash(None, &s, 2).into() + } else { + FnCallHashes::from_native(calc_fn_hash(None, &s, 2)) + }; + op_base.into_fn_call_expr(pos) } _ => { - let pos = args[0].start_position(); - FnCallExpr { args, ..op_base }.into_fn_call_expr(pos) + let pos = op_base.args[0].start_position(); + op_base.into_fn_call_expr(pos) } }; } @@ -3725,8 +3687,7 @@ impl Engine { num_externals + 1, )), args, - pos, - operator_token: None, + op_token: None, capture_parent_scope: false, } .into_fn_call_expr(pos); From dc5e80c821e19318c8189446f0e3b23395e26e2e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 30 Oct 2022 19:01:51 +0800 Subject: [PATCH 079/116] Fix builds. --- src/func/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/func/mod.rs b/src/func/mod.rs index 42ccb91d..0d886513 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -13,8 +13,9 @@ pub mod script; pub use args::FuncArgs; pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; -#[cfg(not(feature = "no_module"))] -pub use call::{ensure_no_data_race, FnCallArgs}; +#[cfg(not(feature = "no_closure"))] +pub use call::ensure_no_data_race; +pub use call::FnCallArgs; pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; From 32493524ed9b5151f09b84265213354d138cd904 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 30 Oct 2022 22:16:09 +0800 Subject: [PATCH 080/116] Code cleanup. --- src/api/custom_syntax.rs | 8 +- src/api/mod.rs | 2 +- src/ast/stmt.rs | 7 +- src/eval/chaining.rs | 20 ++--- src/eval/stmt.rs | 20 +++-- src/func/call.rs | 170 +++++++++++++++++---------------------- src/func/native.rs | 5 +- src/optimizer.rs | 4 +- src/tokenizer.rs | 58 ++++++------- 9 files changed, 144 insertions(+), 150 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 7aaad56b..a12ea593 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -228,7 +228,13 @@ impl Engine { continue; } - let token = Token::lookup_from_syntax(s); + let token = Token::lookup_symbol_from_syntax(s).or_else(|| { + if Token::is_reserved_keyword(s) { + Some(Token::Reserved(Box::new(s.into()))) + } else { + None + } + }); let seg = match s { // Markers not in first position diff --git a/src/api/mod.rs b/src/api/mod.rs index 5f85f140..71973732 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -154,7 +154,7 @@ impl Engine { let keyword = keyword.as_ref(); - match Token::lookup_from_syntax(keyword) { + match Token::lookup_symbol_from_syntax(keyword) { // Standard identifiers and reserved keywords are OK None | Some(Token::Reserved(..)) => (), // custom keywords are OK diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index e272e1e9..541332c3 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -61,7 +61,10 @@ impl OpAssignment { #[must_use] #[inline(always)] pub fn new_op_assignment(name: &str, pos: Position) -> Self { - Self::new_op_assignment_from_token(&Token::lookup_from_syntax(name).expect("operator"), pos) + Self::new_op_assignment_from_token( + &Token::lookup_symbol_from_syntax(name).expect("operator"), + pos, + ) } /// Create a new [`OpAssignment`] from a [`Token`]. /// @@ -90,7 +93,7 @@ impl OpAssignment { #[inline(always)] pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self { Self::new_op_assignment_from_base_token( - &Token::lookup_from_syntax(name).expect("operator"), + &Token::lookup_symbol_from_syntax(name).expect("operator"), pos, ) } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 07c1f676..e7e1e754 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -270,8 +270,8 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, lib, getter, *hash_get, args, is_ref_mut, - false, *pos, level, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, + *pos, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -304,7 +304,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, lib, setter, *hash_set, args, is_ref_mut, false, *pos, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos, level, ) .or_else(|err| match *err { @@ -331,7 +331,7 @@ impl Engine { let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, lib, getter, *hash_get, args, is_ref_mut, false, *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos, level, ) .map_or_else( @@ -430,8 +430,8 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, lib, getter, *hash_get, args, is_ref_mut, - false, pos, level, + global, caches, lib, getter, None, *hash_get, args, + is_ref_mut, pos, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -466,8 +466,8 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_native_fn_call( - global, caches, lib, setter, *hash_set, args, is_ref_mut, - false, pos, level, + global, caches, lib, setter, None, *hash_set, args, + is_ref_mut, pos, level, ) .or_else( |err| match *err { @@ -765,7 +765,7 @@ impl Engine { let level = level + 1; self.exec_native_fn_call( - global, caches, lib, fn_name, hash, args, true, false, pos, level, + global, caches, lib, fn_name, None, hash, args, true, pos, level, ) .map(|(r, ..)| r) } @@ -790,7 +790,7 @@ impl Engine { let level = level + 1; self.exec_native_fn_call( - global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, + global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos, level, ) } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 835b7b91..f61749a8 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -130,8 +130,8 @@ impl Engine { let OpAssignment { hash_op_assign, hash_op, - op_assign, - op, + op_assign: op_assign_token, + op: op_token, pos: op_pos, } = op_info; @@ -142,27 +142,31 @@ impl Engine { let level = level + 1; if self.fast_operators() { - if let Some(func) = get_builtin_op_assignment_fn(op_assign, args[0], args[1]) { + if let Some(func) = get_builtin_op_assignment_fn(op_assign_token, args[0], args[1]) + { // Built-in found - let op = op_assign.literal_syntax(); + let op = op_assign_token.literal_syntax(); let context = (self, op, None, &*global, lib, *op_pos, level).into(); return func(context, args).map(|_| ()); } } - let op_assign = op_assign.literal_syntax(); - let op = op.literal_syntax(); + let op_assign = op_assign_token.literal_syntax(); + let op = op_token.literal_syntax(); + let token = Some(op_assign_token); match self.exec_native_fn_call( - global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, + global, caches, lib, op_assign, token, hash, args, true, *op_pos, level, ) { Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` + let token = Some(op_token); + *args[0] = self .exec_native_fn_call( - global, caches, lib, op, *hash_op, args, true, false, *op_pos, level, + global, caches, lib, op, token, *hash_op, args, true, *op_pos, level, ) .map_err(|err| err.fill_position(op_info.pos))? .0 diff --git a/src/func/call.rs b/src/func/call.rs index b757dda3..882060e9 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -127,48 +127,24 @@ pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) - Ok(()) } -/// Generate the signature for a function call. -#[inline] -#[must_use] -fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynamic]) -> String { - format!( - "{fn_name} ({})", - args.iter() - .map(|a| if a.is::() { - "&str | ImmutableString | String" - } else { - engine.map_type_name(a.type_name()) - }) - .collect::>() - .join(", ") - ) -} - -/// Generate the signature for a namespace-qualified function call. -/// -/// Not available under `no_module`. -#[cfg(not(feature = "no_module"))] -#[inline] -#[must_use] -fn gen_qualified_fn_call_signature( - engine: &Engine, - namespace: &crate::ast::Namespace, - fn_name: &str, - args: &[&mut Dynamic], -) -> String { - let (ns, sep) = ( - namespace.to_string(), - if namespace.is_empty() { - "" - } else { - crate::tokenizer::Token::DoubleColon.literal_syntax() - }, - ); - - format!("{ns}{sep}{}", gen_fn_call_signature(engine, fn_name, args)) -} - impl Engine { + /// Generate the signature for a function call. + #[inline] + #[must_use] + fn gen_fn_call_signature(&self, fn_name: &str, args: &[&mut Dynamic]) -> String { + format!( + "{fn_name} ({})", + args.iter() + .map(|a| if a.is::() { + "&str | ImmutableString | String" + } else { + self.map_type_name(a.type_name()) + }) + .collect::>() + .join(", ") + ) + } + /// Resolve a normal (non-qualified) function call. /// /// Search order: @@ -184,11 +160,10 @@ impl Engine { caches: &'s mut Caches, local_entry: &'s mut Option, lib: &[&Module], - fn_name: &str, + op_token: Option<&Token>, hash_base: u64, args: Option<&mut FnCallArgs>, allow_dynamic: bool, - op_assignment_token: Option<&Token>, ) -> Option<&'s FnResolutionCacheEntry> { if hash_base == 0 { return None; @@ -273,26 +248,25 @@ impl Engine { } // Try to find a built-in version - let builtin = args.and_then(|args| { - if let Some(op_assign) = op_assignment_token { - let (first_arg, rest_args) = args.split_first().unwrap(); + let builtin = + args.and_then(|args| match op_token { + Some(token) if token.is_op_assignment() => { + let (first_arg, rest_args) = args.split_first().unwrap(); - get_builtin_op_assignment_fn(op_assign, *first_arg, rest_args[0]) + get_builtin_op_assignment_fn(token, *first_arg, rest_args[0]) + .map(|f| FnResolutionCacheEntry { + func: CallableFunction::from_fn_builtin(f), + source: None, + }) + } + Some(token) => get_builtin_binary_op_fn(token, args[0], args[1]) .map(|f| FnResolutionCacheEntry { func: CallableFunction::from_fn_builtin(f), source: None, - }) - } else if let Some(ref operator) = Token::lookup_from_syntax(fn_name) { - get_builtin_binary_op_fn(operator, args[0], args[1]).map(|f| { - FnResolutionCacheEntry { - func: CallableFunction::from_fn_builtin(f), - source: None, - } - }) - } else { - None - } - }); + }), + + None => None, + }); return if cache.filter.is_absent_and_set(hash) { // Do not cache "one-hit wonders" @@ -345,21 +319,15 @@ impl Engine { caches: &mut Caches, lib: &[&Module], name: &str, + op_token: Option<&Token>, hash: u64, args: &mut FnCallArgs, is_ref_mut: bool, - is_op_assign: bool, pos: Position, level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { self.track_operation(global, pos)?; - let op_assign = if is_op_assign { - Token::lookup_from_syntax(name) - } else { - None - }; - // Check if function access already in the cache let local_entry = &mut None; @@ -368,11 +336,10 @@ impl Engine { caches, local_entry, lib, - name, + op_token, hash, Some(args), true, - op_assign.as_ref(), ); if func.is_some() { @@ -543,7 +510,7 @@ impl Engine { // Raise error _ => { - Err(ERR::ErrorFunctionNotFound(gen_fn_call_signature(self, name, args), pos).into()) + Err(ERR::ErrorFunctionNotFound(self.gen_fn_call_signature(name, args), pos).into()) } } } @@ -566,6 +533,7 @@ impl Engine { caches: &mut Caches, lib: &[&Module], fn_name: &str, + op_token: Option<&Token>, hashes: FnCallHashes, args: &mut FnCallArgs, is_ref_mut: bool, @@ -646,11 +614,10 @@ impl Engine { caches, local_entry, lib, - fn_name, + None, hashes.script(), None, false, - None, ) .cloned() { @@ -718,8 +685,9 @@ impl Engine { // Native function call let hash = hashes.native(); + self.exec_native_fn_call( - global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, + global, caches, lib, fn_name, op_token, hash, args, is_ref_mut, pos, level, ) } @@ -810,6 +778,7 @@ impl Engine { caches, lib, fn_name, + None, new_hash, &mut args, false, @@ -865,6 +834,7 @@ impl Engine { caches, lib, &fn_name, + None, new_hash, &mut args, is_ref_mut, @@ -965,6 +935,7 @@ impl Engine { caches, lib, fn_name, + None, hash, &mut args, is_ref_mut, @@ -992,10 +963,10 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, + op_token: Option<&Token>, first_arg: Option<&Expr>, args_expr: &[Expr], hashes: FnCallHashes, - is_operator_call: bool, capture_scope: bool, pos: Position, level: usize, @@ -1009,7 +980,7 @@ impl Engine { let redirected; // Handle call() - Redirect function call match name { - _ if is_operator_call => (), + _ if op_token.is_some() => (), // Handle call() KEYWORD_FN_PTR_CALL if total_args >= 1 => { @@ -1204,8 +1175,8 @@ impl Engine { return self .exec_fn_call( - scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, - level, + scope, global, caches, lib, name, op_token, hashes, &mut args, is_ref_mut, + false, pos, level, ) .map(|(v, ..)| v); } @@ -1267,7 +1238,8 @@ impl Engine { } self.exec_fn_call( - None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, + None, global, caches, lib, name, op_token, hashes, &mut args, is_ref_mut, false, pos, + level, ) .map(|(v, ..)| v) } @@ -1441,11 +1413,19 @@ impl Engine { Some(f) => unreachable!("unknown function type: {:?}", f), - None => Err(ERR::ErrorFunctionNotFound( - gen_qualified_fn_call_signature(self, namespace, fn_name, &args), - pos, - ) - .into()), + None => { + let sig = if namespace.is_empty() { + self.gen_fn_call_signature(fn_name, &args) + } else { + format!( + "{namespace}{}{}", + crate::tokenizer::Token::DoubleColon.literal_syntax(), + self.gen_fn_call_signature(fn_name, &args) + ) + }; + + Err(ERR::ErrorFunctionNotFound(sig, pos).into()) + } } } @@ -1507,13 +1487,18 @@ impl Engine { level: usize, ) -> RhaiResult { let FnCallExpr { + #[cfg(not(feature = "no_module"))] + namespace, name, hashes, args, op_token, + capture_parent_scope: capture, .. } = expr; + let op_token = op_token.as_ref(); + // Short-circuit native binary operator call if under Fast Operators mode if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self @@ -1538,16 +1523,16 @@ impl Engine { return self .exec_fn_call( - None, global, caches, lib, name, *hashes, operands, false, false, pos, level, + None, global, caches, lib, name, op_token, *hashes, operands, false, false, + pos, level, ) .map(|(v, ..)| v); } #[cfg(not(feature = "no_module"))] - if !expr.namespace.is_empty() { + if !namespace.is_empty() { // Qualified function call let hash = hashes.native(); - let namespace = &expr.namespace; return self.make_qualified_function_call( scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, @@ -1561,19 +1546,8 @@ impl Engine { ); self.make_function_call( - scope, - global, - caches, - lib, - this_ptr, - name, - first_arg, - args, - *hashes, - expr.op_token.is_some(), - expr.capture_parent_scope, - pos, - level, + scope, global, caches, lib, this_ptr, name, op_token, first_arg, args, *hashes, + *capture, pos, level, ) } } diff --git a/src/func/native.rs b/src/func/native.rs index 8378b075..ba7f5ef8 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -377,6 +377,8 @@ impl<'a> NativeCallContext<'a> { let caches = &mut Caches::new(); let fn_name = fn_name.as_ref(); + let op_token = Token::lookup_symbol_from_syntax(fn_name); + let op_token = op_token.as_ref(); let args_len = args.len(); if native_only { @@ -387,10 +389,10 @@ impl<'a> NativeCallContext<'a> { caches, self.lib, fn_name, + op_token, calc_fn_hash(None, fn_name, args_len), args, is_ref_mut, - false, Position::NONE, self.level + 1, ) @@ -416,6 +418,7 @@ impl<'a> NativeCallContext<'a> { caches, self.lib, fn_name, + op_token, hash, args, is_ref_mut, diff --git a/src/optimizer.rs b/src/optimizer.rs index f0424ed0..451f07d0 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -152,10 +152,10 @@ impl<'a> OptimizerState<'a> { &mut self.caches, lib, fn_name, + None, calc_fn_hash(None, fn_name, arg_values.len()), &mut arg_values.iter_mut().collect::>(), false, - false, Position::NONE, 0, ) @@ -438,7 +438,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b if !x.0.is_op_assignment() && x.1.lhs.is_variable_access(true) && matches!(&x.1.rhs, Expr::FnCall(x2, ..) - if Token::lookup_from_syntax(&x2.name).map_or(false, |t| t.has_op_assignment()) + if Token::lookup_symbol_from_syntax(&x2.name).map_or(false, |t| t.has_op_assignment()) && x2.args.len() == 2 && x2.args[0].get_variable_name(true) == x.1.lhs.get_variable_name(true) ) => diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 96a56170..7dea89d9 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -799,9 +799,9 @@ impl Token { }) } - /// Reverse lookup a token from a piece of syntax. + /// Reverse lookup a symbol token from a piece of syntax. #[must_use] - pub fn lookup_from_syntax(syntax: &str) -> Option { + pub fn lookup_symbol_from_syntax(syntax: &str) -> Option { use Token::*; Some(match syntax { @@ -879,19 +879,11 @@ impl Token { "**" => PowerOf, "**=" => PowerOfAssign, - #[cfg(feature = "no_object")] - "?." => Reserved(Box::new(syntax.into())), - #[cfg(feature = "no_index")] - "?[" => Reserved(Box::new(syntax.into())), - #[cfg(not(feature = "no_function"))] "fn" => Fn, #[cfg(not(feature = "no_function"))] "private" => Private, - #[cfg(feature = "no_function")] - "fn" | "private" => Reserved(Box::new(syntax.into())), - #[cfg(not(feature = "no_module"))] "import" => Import, #[cfg(not(feature = "no_module"))] @@ -899,31 +891,43 @@ impl Token { #[cfg(not(feature = "no_module"))] "as" => As, + _ => return None, + }) + } + + /// Is a piece of syntax a reserved keyword? + #[must_use] + pub fn is_reserved_keyword(syntax: &str) -> bool { + match syntax { + #[cfg(feature = "no_object")] + "?." => true, + #[cfg(feature = "no_index")] + "?[" => true, + #[cfg(feature = "no_function")] + "fn" | "private" => true, #[cfg(feature = "no_module")] - "import" | "export" | "as" => Reserved(Box::new(syntax.into())), + "import" | "export" | "as" => true, // List of reserved operators "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" - | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => { - Reserved(Box::new(syntax.into())) - } + | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => true, // List of reserved keywords "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" - | "async" | "await" | "yield" => Reserved(Box::new(syntax.into())), + | "async" | "await" | "yield" => true, KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { - Reserved(Box::new(syntax.into())) + true } #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN => Reserved(Box::new(syntax.into())), + crate::engine::KEYWORD_IS_DEF_FN => true, - _ => return None, - }) + _ => false, + } } /// Is this token [`EOF`][Token::EOF]? @@ -1708,11 +1712,11 @@ fn get_next_token_inner( // letter or underscore ... #[cfg(not(feature = "unicode-xid-ident"))] ('a'..='z' | '_' | 'A'..='Z', ..) => { - return Some(get_identifier(stream, pos, start_pos, c)); + return Some(get_token_as_identifier(stream, pos, start_pos, c)); } #[cfg(feature = "unicode-xid-ident")] (ch, ..) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => { - return Some(get_identifier(stream, pos, start_pos, c)); + return Some(get_token_as_identifier(stream, pos, start_pos, c)); } // " - string literal @@ -2179,8 +2183,8 @@ fn get_next_token_inner( Some((Token::EOF, *pos)) } -/// Get the next identifier. -fn get_identifier( +/// Get the next token, parsing it as an identifier. +fn get_token_as_identifier( stream: &mut impl InputStream, pos: &mut Position, start_pos: Position, @@ -2199,13 +2203,13 @@ fn get_identifier( } } - let is_valid_identifier = is_valid_identifier(&identifier); - - if let Some(token) = Token::lookup_from_syntax(&identifier) { + if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) { return (token, start_pos); + } else if Token::is_reserved_keyword(&identifier) { + return (Token::Reserved(Box::new(identifier)), start_pos); } - if !is_valid_identifier { + if !is_valid_identifier(&identifier) { return ( Token::LexError(LERR::MalformedIdentifier(identifier.to_string()).into()), start_pos, From 79ba80eeb33d74653297bbe4934b83e62fbd05a8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 30 Oct 2022 23:33:44 +0800 Subject: [PATCH 081/116] Fix full optimization. --- src/ast/expr.rs | 10 ++++++++++ src/optimizer.rs | 14 +++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 52205808..163fdbc9 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -261,6 +261,16 @@ impl FnCallExpr { pub fn into_fn_call_expr(self, pos: Position) -> Expr { Expr::FnCall(self.into(), pos) } + /// Are all arguments constant? + #[inline] + #[must_use] + pub fn constant_args(&self) -> bool { + if self.args.is_empty() { + true + } else { + self.args.iter().all(Expr::is_constant) + } + } } /// A type that wraps a floating-point number and implements [`Hash`]. diff --git a/src/optimizer.rs b/src/optimizer.rs index 451f07d0..6cb4beb2 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -139,6 +139,7 @@ impl<'a> OptimizerState<'a> { pub fn call_fn_with_constant_arguments( &mut self, fn_name: &str, + op_token: Option<&Token>, arg_values: &mut [Dynamic], ) -> Option { #[cfg(not(feature = "no_function"))] @@ -152,7 +153,7 @@ impl<'a> OptimizerState<'a> { &mut self.caches, lib, fn_name, - None, + op_token, calc_fn_hash(None, fn_name, arg_values.len()), &mut arg_values.iter_mut().collect::>(), false, @@ -1098,7 +1099,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { && state.optimization_level == OptimizationLevel::Simple // simple optimizations && x.args.len() == 1 && x.name == KEYWORD_FN_PTR - && x.args[0].is_constant() + && x.constant_args() => { let fn_name = match x.args[0] { Expr::StringConstant(ref s, ..) => s.clone().into(), @@ -1122,8 +1123,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { Expr::FnCall(x, pos) if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations - && x.args.iter().all(Expr::is_constant) // all arguments are constants - //&& !is_valid_identifier(x.chars()) // cannot be scripted + && x.constant_args() // all arguments are constants => { let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::>(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); @@ -1186,11 +1186,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { Expr::FnCall(x, pos) if !x.is_qualified() // non-qualified && state.optimization_level == OptimizationLevel::Full // full optimizations - && x.args.iter().all(Expr::is_constant) // all arguments are constants + && x.constant_args() // all arguments are constants => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some(); + let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some(); #[cfg(feature = "no_function")] let has_script_fn = false; @@ -1201,7 +1201,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), #[cfg(not(feature = "no_closure"))] crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), - _ => state.call_fn_with_constant_arguments(&x.name, arg_values) + _ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values) }; if let Some(result) = result { From 1282beabbb7490126cab257a1e5e725e8cfb7f43 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 31 Oct 2022 18:07:38 +0800 Subject: [PATCH 082/116] Handle type formatting of Result<..., Box> --- src/api/type_names.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/api/type_names.rs b/src/api/type_names.rs index f7897bf0..dda879fc 100644 --- a/src/api/type_names.rs +++ b/src/api/type_names.rs @@ -108,11 +108,15 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str { /// Format a Rust type to be display-friendly. /// +/// * `rhai::` prefix is cleared. /// * `()` is cleared. +/// * `&mut` is cleared. /// * `INT` and `FLOAT` are expanded. /// * [`RhaiResult`][crate::RhaiResult] and [`RhaiResultOf`][crate::RhaiResultOf] are expanded. #[cfg(feature = "metadata")] pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow { + const RESULT_TYPE: &str = "Result<"; + const ERROR_TYPE: &str = ",Box>"; const RHAI_RESULT_TYPE: &str = "RhaiResult"; const RHAI_RESULT_TYPE_EXPAND: &str = "Result>"; const RHAI_RESULT_OF_TYPE: &str = "RhaiResultOf<"; @@ -135,6 +139,10 @@ pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow { } else { format!("&mut {r}").into() }; + } else if typ.contains(" ") { + let typ = typ.replace(" ", ""); + let r = format_type(&typ, is_return_type); + return r.into_owned().into(); } match typ { @@ -167,6 +175,12 @@ pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow { .replace("{}", format_type(inner, false).trim()) .into() } + ty if ty.starts_with(RESULT_TYPE) && ty.ends_with(ERROR_TYPE) => { + let inner = &ty[RESULT_TYPE.len()..ty.len() - ERROR_TYPE.len()]; + RHAI_RESULT_OF_TYPE_EXPAND + .replace("{}", format_type(inner, false).trim()) + .into() + } ty => ty.into(), } } From 3e6a3a2759dba46ae976497e65c89040311edfa3 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Mon, 31 Oct 2022 22:14:09 +0900 Subject: [PATCH 083/116] Stable Hash: Add feature, adjust CI, modify build.rs, add new API for accessing and setting the ahash seed in config.rs, make config.rs public --- .github/workflows/build.yml | 31 +++++++++++++++++++ .gitignore | 3 ++ Cargo.toml | 1 + build.rs | 25 +++++++-------- build.template | 61 ++++++++++++++++++++++++++++++++++--- src/config.rs | 53 +++++++++++++++++++++++++++++--- src/func/hashing.rs | 2 +- src/lib.rs | 4 ++- 8 files changed, 157 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6823d676..11ca384f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,6 +89,37 @@ jobs: command: test args: ${{matrix.flags}} + nightly_features_build: + name: NightlyFeatureBuild + runs-on: ${{matrix.os}} + continue-on-error: ${{matrix.experimental}} + strategy: + matrix: + os: [ubuntu-latest] + flags: + - "--features stable_hash" + toolchain: [ nightly ] + experimental: [ false ] + include: + # smoketests for different toolchains + - { toolchain: nightly, os: windows-latest, experimental: false, flags: "" } + - { toolchain: nightly, os: macos-latest, experimental: false, flags: "" } + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{matrix.toolchain}} + override: true + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + args: ${{matrix.flags}} + + # no-std builds are a bit more extensive to test no_std_build: name: NoStdBuild diff --git a/.gitignore b/.gitignore index fe87b992..10e86a86 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Rhai.toml **/*.bat doc/rhai-sync.json doc/rhai.json +.idea/ +.idea +.idea/* diff --git a/Cargo.toml b/Cargo.toml index 3999b7de..310ceb40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures debugging = ["internals"] # enable debugging +stable_hash = ["std"] serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std diff --git a/build.rs b/build.rs index 66903154..76abd9ee 100644 --- a/build.rs +++ b/build.rs @@ -8,20 +8,21 @@ fn main() { // Tell Cargo that if the given environment variable changes, to rerun this build script. println!("cargo:rerun-if-changed=build.template"); println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); + if !cfg!(feature = "stable_hash") { + let mut contents = String::new(); - let mut contents = String::new(); + File::open("build.template") + .expect("cannot open `build.template`") + .read_to_string(&mut contents) + .expect("cannot read from `build.template`"); - File::open("build.template") - .expect("cannot open `build.template`") - .read_to_string(&mut contents) - .expect("cannot read from `build.template`"); + let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); - let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); + contents = contents.replace("{{AHASH_SEED}}", &seed); - contents = contents.replace("{{AHASH_SEED}}", &seed); - - File::create("src/config.rs") - .expect("cannot create `config.rs`") - .write(contents.as_bytes()) - .expect("cannot write to `config.rs`"); + File::create("src/config.rs") + .expect("cannot create `config.rs`") + .write_all(contents.as_bytes()) + .expect("cannot write to `config.rs`"); + } } diff --git a/build.template b/build.template index 6f09909f..ed5e161d 100644 --- a/build.template +++ b/build.template @@ -1,7 +1,60 @@ -//! Configuration settings for this Rhai build -//! -//! This file is auto-generated from `build.template` +#[cfg(feature = "stable_hash")] +use core::cell::OnceCell; +// [236,800,954,213], haha funny yume nikki reference + +#[cfg(not(feature = "stable_hash"))] /// Fixed hashing seeds for stable hashing. +/// /// Set to [`None`] to disable stable hashing. -pub const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; +/// +/// See [`set_rhai_ahash_seed`] for more. +/// +/// You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +/// environment variable instead. +/// +/// E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; +#[cfg(feature = "stable_hash")] +/// Fixed hashing seeds for stable hashing. +/// +/// Set to [`None`] to disable stable hashing. +/// +/// See [`set_rhai_ahash_seed`] for more. +/// +/// You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +/// environment variable instead. +/// +/// E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +static AHASH_SEED: OnceCell> = OnceCell::new(); + +#[cfg(feature = "stable_hash")] +#[doc(cfg(feature = "stable_hash"))] +/// Sets the Rhai Ahash seed. This is used to hash functions and the like. +/// +/// This is a global variable, and thus will affect every Rhai instance. +/// This should not be used _unless_ you know you need it. +/// +/// **WARNING**: +/// - You can only call this function **ONCE** for the whole of your program execution. +/// - You should gracefully handle the `Err(())`. +/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) +} + +#[cfg(feature = "stable_hash")] +/// Gets the current Rhai Ahash Seed. +/// +/// See [`set_rhai_ahash_seed`] for more. +pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED.get().map(|x| *x).flatten() +} + +#[cfg(not(feature = "stable_hash"))] +/// Gets the current Rhai Ahash Seed. +/// +/// See [`AHASH_SEED`] and [`set_rhai_ahash_seed`] for more. +pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 93db79d5..4297fa07 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,50 @@ -//! Configuration settings for this Rhai build +//! Fixed hashing seeds for stable hashing. //! -//! This file is auto-generated from `build.template` +//! Set to [`None`] to disable stable hashing. +//! +//! See [`set_rhai_ahash_seed`] for more. +//! +//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +//! environment variable instead. +//! +//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -/// Fixed hashing seeds for stable hashing. -/// Set to [`None`] to disable stable hashing. -pub const AHASH_SEED: Option<[u64; 4]> = None; +#[cfg(feature = "stable_hash")] +use std::sync::OnceLock; + +#[cfg(not(feature = "stable_hash"))] +const AHASH_SEED: Option<[u64; 4]> = None; +#[cfg(feature = "stable_hash")] +static AHASH_SEED: OnceLock> = OnceLock::new(); + +#[cfg(feature = "stable_hash")] +// #[doc(cfg(feature = "stable_hash"))] +/// Sets the Rhai Ahash seed. This is used to hash functions and the like. +/// +/// This is a global variable, and thus will affect every Rhai instance. +/// This should not be used _unless_ you know you need it. +/// +/// **WARNING**: +/// - You can only call this function **ONCE** for the whole of your program execution. +/// - You should gracefully handle the `Err(())`. +/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) +} + +#[cfg(feature = "stable_hash")] +/// Gets the current Rhai Ahash Seed. +/// +/// See [`set_rhai_ahash_seed`] for more. +pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED.get().map(|x| *x).flatten() +} + +#[cfg(not(feature = "stable_hash"))] +/// Gets the current Rhai Ahash Seed. +/// +/// See [`AHASH_SEED`] and [`set_rhai_ahash_seed`] for more. +pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED +} diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 5f6a806c..406a21c6 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -77,7 +77,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - match config::AHASH_SEED { + match config::get_rhai_ahash_seed() { Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() } diff --git a/src/lib.rs b/src/lib.rs index 199edb75..d2d6eae7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,8 @@ #![allow(clippy::negative_feature_names)] #![allow(clippy::module_inception)] +#![cfg_attr(feature = "stable_hash", feature(once_cell))] + #[cfg(feature = "no_std")] extern crate alloc; @@ -83,7 +85,7 @@ use std::prelude::v1::*; // Internal modules mod api; mod ast; -mod config; +pub mod config; mod engine; mod eval; mod func; From ec332b4e496189efe899c1e0ba4d1f789df38b55 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Mon, 31 Oct 2022 22:20:43 +0900 Subject: [PATCH 084/116] add errors doc for set rhai fn --- src/config.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config.rs b/src/config.rs index 4297fa07..9ea638bb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -29,6 +29,9 @@ static AHASH_SEED: OnceLock> = OnceLock::new(); /// - You can only call this function **ONCE** for the whole of your program execution. /// - You should gracefully handle the `Err(())`. /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +/// +/// # Errors +/// This will error if the AHashSeed is already set. pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { AHASH_SEED.set(new_seed) } From 87e51cb8fe7ce30396a2c2eb38b97027ef10598b Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Mon, 31 Oct 2022 22:26:04 +0900 Subject: [PATCH 085/116] adjust warnings doc --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 9ea638bb..e7611418 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,7 +25,7 @@ static AHASH_SEED: OnceLock> = OnceLock::new(); /// This is a global variable, and thus will affect every Rhai instance. /// This should not be used _unless_ you know you need it. /// -/// **WARNING**: +/// # Warning: /// - You can only call this function **ONCE** for the whole of your program execution. /// - You should gracefully handle the `Err(())`. /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). From bb01f914e012500653e9a2a2e189e6fbeae8a8ea Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Tue, 1 Nov 2022 00:59:07 +0900 Subject: [PATCH 086/116] Address concerns --- .github/workflows/build.yml | 63 ++++++------- Cargo.toml | 2 +- build.rs | 4 +- build.template | 175 +++++++++++++++++++++++++----------- src/config.rs | 148 ++++++++++++++++++++++-------- src/config/hashing.rs | 129 ++++++++++++++++++++++++++ src/config/mod.rs | 3 + src/func/hashing.rs | 2 +- src/lib.rs | 2 +- 9 files changed, 404 insertions(+), 124 deletions(-) create mode 100644 src/config/hashing.rs create mode 100644 src/config/mod.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11ca384f..fc782151 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: flags: - "" - "--features debugging" - - "--features metadata,serde,internals" + - "--features metadata,serde,internals,static_hash" - "--features unchecked,serde,metadata,internals,debugging" - "--features sync,serde,metadata,internals,debugging" - "--features no_position,serde,metadata,internals,debugging" @@ -89,35 +89,35 @@ jobs: command: test args: ${{matrix.flags}} - nightly_features_build: - name: NightlyFeatureBuild - runs-on: ${{matrix.os}} - continue-on-error: ${{matrix.experimental}} - strategy: - matrix: - os: [ubuntu-latest] - flags: - - "--features stable_hash" - toolchain: [ nightly ] - experimental: [ false ] - include: - # smoketests for different toolchains - - { toolchain: nightly, os: windows-latest, experimental: false, flags: "" } - - { toolchain: nightly, os: macos-latest, experimental: false, flags: "" } - fail-fast: false - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{matrix.toolchain}} - override: true - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: ${{matrix.flags}} +# nightly_features_build: +# name: NightlyFeatureBuild +# runs-on: ${{matrix.os}} +# continue-on-error: ${{matrix.experimental}} +# strategy: +# matrix: +# os: [ubuntu-latest] +# flags: +# - "--features stable_hash" +# toolchain: [ nightly ] +# experimental: [ false ] +# include: +# # smoketests for different toolchains +# - { toolchain: nightly, os: windows-latest, experimental: false, flags: "" } +# - { toolchain: nightly, os: macos-latest, experimental: false, flags: "" } +# fail-fast: false +# steps: +# - name: Checkout +# uses: actions/checkout@v2 +# - name: Setup Toolchain +# uses: actions-rs/toolchain@v1 +# with: +# toolchain: ${{matrix.toolchain}} +# override: true +# - name: Test +# uses: actions-rs/cargo@v1 +# with: +# command: test +# args: ${{matrix.flags}} # no-std builds are a bit more extensive to test @@ -127,6 +127,9 @@ jobs: continue-on-error: ${{matrix.experimental}} strategy: matrix: + flags: + - "" + - "--features static_hash" include: - {os: ubuntu-latest, flags: "--profile unix", experimental: false} - {os: windows-latest, flags: "--profile windows", experimental: true} diff --git a/Cargo.toml b/Cargo.toml index 310ceb40..e5edb293 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures debugging = ["internals"] # enable debugging -stable_hash = ["std"] +static_hash = [] serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std diff --git a/build.rs b/build.rs index 76abd9ee..dda0850e 100644 --- a/build.rs +++ b/build.rs @@ -20,9 +20,9 @@ fn main() { contents = contents.replace("{{AHASH_SEED}}", &seed); - File::create("src/config.rs") + File::create("src/config/hashing.rs") .expect("cannot create `config.rs`") .write_all(contents.as_bytes()) - .expect("cannot write to `config.rs`"); + .expect("cannot write to `config/hashing.rs`"); } } diff --git a/build.template b/build.template index ed5e161d..709a3f65 100644 --- a/build.template +++ b/build.template @@ -1,60 +1,129 @@ -#[cfg(feature = "stable_hash")] -use core::cell::OnceCell; +//! Fixed hashing seeds for stable hashing. +//! +//! Set to [`None`] to disable stable hashing. +//! +//! See [`set_rhai_ahash_seed`] for more. +//! +//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +//! environment variable instead. +//! +//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -// [236,800,954,213], haha funny yume nikki reference +pub use internal::get_ahash_seed; +#[cfg(feature = "static_hash")] +pub use internal::set_ahash_seed; -#[cfg(not(feature = "stable_hash"))] -/// Fixed hashing seeds for stable hashing. -/// -/// Set to [`None`] to disable stable hashing. -/// -/// See [`set_rhai_ahash_seed`] for more. -/// -/// You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` -/// environment variable instead. -/// -/// E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` -const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; -#[cfg(feature = "stable_hash")] -/// Fixed hashing seeds for stable hashing. -/// -/// Set to [`None`] to disable stable hashing. -/// -/// See [`set_rhai_ahash_seed`] for more. -/// -/// You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` -/// environment variable instead. -/// -/// E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` -static AHASH_SEED: OnceCell> = OnceCell::new(); +#[cfg(feature = "static_hash")] +mod internal { + use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + panic::{RefUnwindSafe, UnwindSafe} + }; -#[cfg(feature = "stable_hash")] -#[doc(cfg(feature = "stable_hash"))] -/// Sets the Rhai Ahash seed. This is used to hash functions and the like. -/// -/// This is a global variable, and thus will affect every Rhai instance. -/// This should not be used _unless_ you know you need it. -/// -/// **WARNING**: -/// - You can only call this function **ONCE** for the whole of your program execution. -/// - You should gracefully handle the `Err(())`. -/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). -pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + struct SusLock where T: 'static { + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, + } + + impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData + } + } + + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some( + unsafe { + (&*self.data.get()).assume_init_ref() + } + ) + } else { + return None + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } + } + + unsafe impl Sync for SusLock {} + unsafe impl Send for SusLock {} + impl RefUnwindSafe for SusLock {} + + impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } + } + + static AHASH_SEED: SusLock> = SusLock::new(); + + // #[doc(cfg(feature = "stable_hash"))] + /// Sets the Rhai Ahash seed. This is used to hash functions and the like. + /// + /// This is a global variable, and thus will affect every Rhai instance. + /// This should not be used _unless_ you know you need it. + /// + /// # Warnings + /// - You can only call this function **ONCE** for the whole of your program execution. + /// - You should gracefully handle the `Err(())`. + /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). + /// + /// # Errors + /// This will error if the AHashSeed is already set. + pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) + } + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_rhai_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; + + AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + } } -#[cfg(feature = "stable_hash")] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED.get().map(|x| *x).flatten() -} +#[cfg(not(feature = "static_hash"))] +mod internal { + const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; -#[cfg(not(feature = "stable_hash"))] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`AHASH_SEED`] and [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED + } } \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index e7611418..709a3f65 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,44 +10,120 @@ //! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -#[cfg(feature = "stable_hash")] -use std::sync::OnceLock; +pub use internal::get_ahash_seed; +#[cfg(feature = "static_hash")] +pub use internal::set_ahash_seed; -#[cfg(not(feature = "stable_hash"))] -const AHASH_SEED: Option<[u64; 4]> = None; -#[cfg(feature = "stable_hash")] -static AHASH_SEED: OnceLock> = OnceLock::new(); +#[cfg(feature = "static_hash")] +mod internal { + use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + panic::{RefUnwindSafe, UnwindSafe} + }; -#[cfg(feature = "stable_hash")] -// #[doc(cfg(feature = "stable_hash"))] -/// Sets the Rhai Ahash seed. This is used to hash functions and the like. -/// -/// This is a global variable, and thus will affect every Rhai instance. -/// This should not be used _unless_ you know you need it. -/// -/// # Warning: -/// - You can only call this function **ONCE** for the whole of your program execution. -/// - You should gracefully handle the `Err(())`. -/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). -/// -/// # Errors -/// This will error if the AHashSeed is already set. -pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + struct SusLock where T: 'static { + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, + } + + impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData + } + } + + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some( + unsafe { + (&*self.data.get()).assume_init_ref() + } + ) + } else { + return None + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } + } + + unsafe impl Sync for SusLock {} + unsafe impl Send for SusLock {} + impl RefUnwindSafe for SusLock {} + + impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } + } + + static AHASH_SEED: SusLock> = SusLock::new(); + + // #[doc(cfg(feature = "stable_hash"))] + /// Sets the Rhai Ahash seed. This is used to hash functions and the like. + /// + /// This is a global variable, and thus will affect every Rhai instance. + /// This should not be used _unless_ you know you need it. + /// + /// # Warnings + /// - You can only call this function **ONCE** for the whole of your program execution. + /// - You should gracefully handle the `Err(())`. + /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). + /// + /// # Errors + /// This will error if the AHashSeed is already set. + pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) + } + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_rhai_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; + + AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + } } -#[cfg(feature = "stable_hash")] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED.get().map(|x| *x).flatten() -} +#[cfg(not(feature = "static_hash"))] +mod internal { + const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; -#[cfg(not(feature = "stable_hash"))] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`AHASH_SEED`] and [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED -} + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED + } +} \ No newline at end of file diff --git a/src/config/hashing.rs b/src/config/hashing.rs new file mode 100644 index 00000000..709a3f65 --- /dev/null +++ b/src/config/hashing.rs @@ -0,0 +1,129 @@ +//! Fixed hashing seeds for stable hashing. +//! +//! Set to [`None`] to disable stable hashing. +//! +//! See [`set_rhai_ahash_seed`] for more. +//! +//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +//! environment variable instead. +//! +//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 + +pub use internal::get_ahash_seed; +#[cfg(feature = "static_hash")] +pub use internal::set_ahash_seed; + +#[cfg(feature = "static_hash")] +mod internal { + use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + panic::{RefUnwindSafe, UnwindSafe} + }; + + struct SusLock where T: 'static { + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, + } + + impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData + } + } + + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some( + unsafe { + (&*self.data.get()).assume_init_ref() + } + ) + } else { + return None + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } + } + + unsafe impl Sync for SusLock {} + unsafe impl Send for SusLock {} + impl RefUnwindSafe for SusLock {} + + impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } + } + + static AHASH_SEED: SusLock> = SusLock::new(); + + // #[doc(cfg(feature = "stable_hash"))] + /// Sets the Rhai Ahash seed. This is used to hash functions and the like. + /// + /// This is a global variable, and thus will affect every Rhai instance. + /// This should not be used _unless_ you know you need it. + /// + /// # Warnings + /// - You can only call this function **ONCE** for the whole of your program execution. + /// - You should gracefully handle the `Err(())`. + /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). + /// + /// # Errors + /// This will error if the AHashSeed is already set. + pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) + } + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_rhai_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; + + AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + } +} + +#[cfg(not(feature = "static_hash"))] +mod internal { + const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED + } +} \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 00000000..194ac6da --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,3 @@ +//! Contains Configuration for Rhai. + +pub mod hashing; \ No newline at end of file diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 406a21c6..855dcd3d 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -77,7 +77,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - match config::get_rhai_ahash_seed() { + match config::hashing::get_ahash_seed() { Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() } diff --git a/src/lib.rs b/src/lib.rs index d2d6eae7..f2a0cad5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ #![allow(clippy::negative_feature_names)] #![allow(clippy::module_inception)] -#![cfg_attr(feature = "stable_hash", feature(once_cell))] +#![cfg_attr(feature = "static_hash", feature(once_cell))] #[cfg(feature = "no_std")] extern crate alloc; From 876fd63dd3c359c597bdcd71b02e01346d2db91b Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Tue, 1 Nov 2022 01:01:04 +0900 Subject: [PATCH 087/116] remove unneeded feature --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f2a0cad5..8879012f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,8 +71,6 @@ #![allow(clippy::negative_feature_names)] #![allow(clippy::module_inception)] -#![cfg_attr(feature = "static_hash", feature(once_cell))] - #[cfg(feature = "no_std")] extern crate alloc; From 488a8eab5e015f55f51eb01e0c2e5bb97494e289 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Tue, 1 Nov 2022 01:02:55 +0900 Subject: [PATCH 088/116] remove config.rs (?) --- src/config.rs | 129 -------------------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 src/config.rs diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 709a3f65..00000000 --- a/src/config.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Fixed hashing seeds for stable hashing. -//! -//! Set to [`None`] to disable stable hashing. -//! -//! See [`set_rhai_ahash_seed`] for more. -//! -//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` -//! environment variable instead. -//! -//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` -// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 - -pub use internal::get_ahash_seed; -#[cfg(feature = "static_hash")] -pub use internal::set_ahash_seed; - -#[cfg(feature = "static_hash")] -mod internal { - use core::{ - cell::UnsafeCell, - marker::PhantomData, - mem::MaybeUninit, - sync::atomic::{AtomicBool, Ordering}, - panic::{RefUnwindSafe, UnwindSafe} - }; - - struct SusLock where T: 'static { - initalized: AtomicBool, - data: UnsafeCell>, - _marker: PhantomData, - } - - impl SusLock { - pub const fn new() -> SusLock { - SusLock { - initalized: AtomicBool::new(false), - data: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData - } - } - - pub fn get(&self) -> Option<&T> { - if self.initalized.load(Ordering::SeqCst) { - Some( - unsafe { - (&*self.data.get()).assume_init_ref() - } - ) - } else { - return None - } - } - - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { - let value = f(); - if !self.initalized.load(Ordering::SeqCst) { - unsafe { - self.data.get().write(MaybeUninit::new(value)); - } - self.initalized.store(true, Ordering::SeqCst); - } - - self.get() - } - - pub fn set(&self, value: T) -> Result<(), T> { - if self.initalized.load(Ordering::SeqCst) { - Err(value) - } else { - let _ = self.get_or_init(|| value); - Ok(()) - } - } - } - - unsafe impl Sync for SusLock {} - unsafe impl Send for SusLock {} - impl RefUnwindSafe for SusLock {} - - impl Drop for SusLock { - fn drop(&mut self) { - if self.initalized.load(Ordering::SeqCst) { - unsafe { (&mut *self.data.get()).assume_init_drop() }; - } - } - } - - static AHASH_SEED: SusLock> = SusLock::new(); - - // #[doc(cfg(feature = "stable_hash"))] - /// Sets the Rhai Ahash seed. This is used to hash functions and the like. - /// - /// This is a global variable, and thus will affect every Rhai instance. - /// This should not be used _unless_ you know you need it. - /// - /// # Warnings - /// - You can only call this function **ONCE** for the whole of your program execution. - /// - You should gracefully handle the `Err(())`. - /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). - /// - /// # Errors - /// This will error if the AHashSeed is already set. - pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) - } - - /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. - /// The default seed is not stable and may change between versions. - /// - /// See [`set_rhai_ahash_seed`] for more. - pub fn get_ahash_seed() -> Option<[u64; 4]> { - const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; - - AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() - } -} - -#[cfg(not(feature = "static_hash"))] -mod internal { - const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; - - /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. - /// The default seed is not stable and may change between versions. - /// - /// See [`set_ahash_seed`] for more. - pub fn get_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED - } -} \ No newline at end of file From 79e7c305bf86193846f050858e68f6946c23a0c8 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Tue, 1 Nov 2022 01:26:23 +0900 Subject: [PATCH 089/116] fix templateing issue --- build.rs | 2 +- src/config/hashing.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index dda0850e..a994ef69 100644 --- a/build.rs +++ b/build.rs @@ -18,7 +18,7 @@ fn main() { let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); - contents = contents.replace("{{AHASH_SEED}}", &seed); + contents = contents.replace("{{ AHASH_SEED }}", &seed); File::create("src/config/hashing.rs") .expect("cannot create `config.rs`") diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 709a3f65..19423149 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -117,7 +117,7 @@ mod internal { #[cfg(not(feature = "static_hash"))] mod internal { - const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; + const AHASH_SEED: Option<[u64; 4]> = None; /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. /// The default seed is not stable and may change between versions. From 5816d571b39d6da8cf357a24175399fdc9067c19 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Tue, 1 Nov 2022 21:25:45 +0900 Subject: [PATCH 090/116] remove stable hash and fix CI --- Cargo.toml | 1 - build.rs | 24 +++--- build.template | 185 +++++++++++++++++++----------------------- src/config/hashing.rs | 185 +++++++++++++++++++----------------------- src/config/mod.rs | 2 +- 5 files changed, 180 insertions(+), 217 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5edb293..3999b7de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,6 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures debugging = ["internals"] # enable debugging -static_hash = [] serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std diff --git a/build.rs b/build.rs index a994ef69..e95b1dc3 100644 --- a/build.rs +++ b/build.rs @@ -8,21 +8,19 @@ fn main() { // Tell Cargo that if the given environment variable changes, to rerun this build script. println!("cargo:rerun-if-changed=build.template"); println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); - if !cfg!(feature = "stable_hash") { - let mut contents = String::new(); + let mut contents = String::new(); - File::open("build.template") - .expect("cannot open `build.template`") - .read_to_string(&mut contents) - .expect("cannot read from `build.template`"); + File::open("build.template") + .expect("cannot open `build.template`") + .read_to_string(&mut contents) + .expect("cannot read from `build.template`"); - let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); + let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); - contents = contents.replace("{{ AHASH_SEED }}", &seed); + contents = contents.replace("{{ AHASH_SEED }}", &seed); - File::create("src/config/hashing.rs") - .expect("cannot create `config.rs`") - .write_all(contents.as_bytes()) - .expect("cannot write to `config/hashing.rs`"); - } + File::create("src/config/hashing.rs") + .expect("cannot create `config.rs`") + .write_all(contents.as_bytes()) + .expect("cannot write to `config/hashing.rs`"); } diff --git a/build.template b/build.template index 709a3f65..89b0e403 100644 --- a/build.template +++ b/build.template @@ -10,120 +10,103 @@ //! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -pub use internal::get_ahash_seed; -#[cfg(feature = "static_hash")] -pub use internal::set_ahash_seed; +use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + panic::{RefUnwindSafe, UnwindSafe}, + sync::atomic::{AtomicBool, Ordering}, +}; -#[cfg(feature = "static_hash")] -mod internal { - use core::{ - cell::UnsafeCell, - marker::PhantomData, - mem::MaybeUninit, - sync::atomic::{AtomicBool, Ordering}, - panic::{RefUnwindSafe, UnwindSafe} - }; +// Safety: lol +struct SusLock +where + T: 'static, +{ + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, +} - struct SusLock where T: 'static { - initalized: AtomicBool, - data: UnsafeCell>, - _marker: PhantomData, - } - - impl SusLock { - pub const fn new() -> SusLock { - SusLock { - initalized: AtomicBool::new(false), - data: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData - } - } - - pub fn get(&self) -> Option<&T> { - if self.initalized.load(Ordering::SeqCst) { - Some( - unsafe { - (&*self.data.get()).assume_init_ref() - } - ) - } else { - return None - } - } - - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { - let value = f(); - if !self.initalized.load(Ordering::SeqCst) { - unsafe { - self.data.get().write(MaybeUninit::new(value)); - } - self.initalized.store(true, Ordering::SeqCst); - } - - self.get() - } - - pub fn set(&self, value: T) -> Result<(), T> { - if self.initalized.load(Ordering::SeqCst) { - Err(value) - } else { - let _ = self.get_or_init(|| value); - Ok(()) - } +impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData, } } - unsafe impl Sync for SusLock {} - unsafe impl Send for SusLock {} - impl RefUnwindSafe for SusLock {} - - impl Drop for SusLock { - fn drop(&mut self) { - if self.initalized.load(Ordering::SeqCst) { - unsafe { (&mut *self.data.get()).assume_init_drop() }; - } + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some(unsafe { (&*self.data.get()).assume_init_ref() }) + } else { + return None; } } - static AHASH_SEED: SusLock> = SusLock::new(); + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } - // #[doc(cfg(feature = "stable_hash"))] - /// Sets the Rhai Ahash seed. This is used to hash functions and the like. - /// - /// This is a global variable, and thus will affect every Rhai instance. - /// This should not be used _unless_ you know you need it. - /// - /// # Warnings - /// - You can only call this function **ONCE** for the whole of your program execution. - /// - You should gracefully handle the `Err(())`. - /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). - /// - /// # Errors - /// This will error if the AHashSeed is already set. - pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + self.get() } - /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. - /// The default seed is not stable and may change between versions. - /// - /// See [`set_rhai_ahash_seed`] for more. - pub fn get_ahash_seed() -> Option<[u64; 4]> { - const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; - - AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } } } -#[cfg(not(feature = "static_hash"))] -mod internal { - const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; +unsafe impl Sync for SusLock {} +unsafe impl Send for SusLock {} +impl RefUnwindSafe for SusLock {} - /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. - /// The default seed is not stable and may change between versions. - /// - /// See [`set_ahash_seed`] for more. - pub fn get_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED +impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } } -} \ No newline at end of file +} + +static AHASH_SEED: SusLock> = SusLock::new(); + +// #[doc(cfg(feature = "stable_hash"))] +/// Sets the Rhai Ahash seed. This is used to hash functions and the like. +/// +/// This is a global variable, and thus will affect every Rhai instance. +/// This should not be used _unless_ you know you need it. +/// +/// # Warnings +/// - You can only call this function **ONCE** for the whole of your program execution. +/// - You should gracefully handle the `Err(())`. +/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +/// +/// # Errors +/// This will error if the AHashSeed is already set. +pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) +} + +/// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. +/// The default seed is not stable and may change between versions. +/// +/// See [`set_rhai_ahash_seed`] for more. +pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: Option<[u64; 4]> = {{ AHASH_SEED }}; + + AHASH_SEED + .get_or_init(|| FUNNY_YUMENIKKI_REFERENCE) + .map(|x| *x) + .flatten() +} diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 19423149..3f318fef 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -10,120 +10,103 @@ //! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -pub use internal::get_ahash_seed; -#[cfg(feature = "static_hash")] -pub use internal::set_ahash_seed; +use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + panic::{RefUnwindSafe, UnwindSafe}, + sync::atomic::{AtomicBool, Ordering}, +}; -#[cfg(feature = "static_hash")] -mod internal { - use core::{ - cell::UnsafeCell, - marker::PhantomData, - mem::MaybeUninit, - sync::atomic::{AtomicBool, Ordering}, - panic::{RefUnwindSafe, UnwindSafe} - }; +// Safety: lol +struct SusLock +where + T: 'static, +{ + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, +} - struct SusLock where T: 'static { - initalized: AtomicBool, - data: UnsafeCell>, - _marker: PhantomData, - } - - impl SusLock { - pub const fn new() -> SusLock { - SusLock { - initalized: AtomicBool::new(false), - data: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData - } - } - - pub fn get(&self) -> Option<&T> { - if self.initalized.load(Ordering::SeqCst) { - Some( - unsafe { - (&*self.data.get()).assume_init_ref() - } - ) - } else { - return None - } - } - - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { - let value = f(); - if !self.initalized.load(Ordering::SeqCst) { - unsafe { - self.data.get().write(MaybeUninit::new(value)); - } - self.initalized.store(true, Ordering::SeqCst); - } - - self.get() - } - - pub fn set(&self, value: T) -> Result<(), T> { - if self.initalized.load(Ordering::SeqCst) { - Err(value) - } else { - let _ = self.get_or_init(|| value); - Ok(()) - } +impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData, } } - unsafe impl Sync for SusLock {} - unsafe impl Send for SusLock {} - impl RefUnwindSafe for SusLock {} - - impl Drop for SusLock { - fn drop(&mut self) { - if self.initalized.load(Ordering::SeqCst) { - unsafe { (&mut *self.data.get()).assume_init_drop() }; - } + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some(unsafe { (&*self.data.get()).assume_init_ref() }) + } else { + return None; } } - static AHASH_SEED: SusLock> = SusLock::new(); + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } - // #[doc(cfg(feature = "stable_hash"))] - /// Sets the Rhai Ahash seed. This is used to hash functions and the like. - /// - /// This is a global variable, and thus will affect every Rhai instance. - /// This should not be used _unless_ you know you need it. - /// - /// # Warnings - /// - You can only call this function **ONCE** for the whole of your program execution. - /// - You should gracefully handle the `Err(())`. - /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). - /// - /// # Errors - /// This will error if the AHashSeed is already set. - pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + self.get() } - /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. - /// The default seed is not stable and may change between versions. - /// - /// See [`set_rhai_ahash_seed`] for more. - pub fn get_ahash_seed() -> Option<[u64; 4]> { - const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; - - AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } } } -#[cfg(not(feature = "static_hash"))] -mod internal { - const AHASH_SEED: Option<[u64; 4]> = None; +unsafe impl Sync for SusLock {} +unsafe impl Send for SusLock {} +impl RefUnwindSafe for SusLock {} - /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. - /// The default seed is not stable and may change between versions. - /// - /// See [`set_ahash_seed`] for more. - pub fn get_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED +impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } } -} \ No newline at end of file +} + +static AHASH_SEED: SusLock> = SusLock::new(); + +// #[doc(cfg(feature = "stable_hash"))] +/// Sets the Rhai Ahash seed. This is used to hash functions and the like. +/// +/// This is a global variable, and thus will affect every Rhai instance. +/// This should not be used _unless_ you know you need it. +/// +/// # Warnings +/// - You can only call this function **ONCE** for the whole of your program execution. +/// - You should gracefully handle the `Err(())`. +/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +/// +/// # Errors +/// This will error if the AHashSeed is already set. +pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) +} + +/// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. +/// The default seed is not stable and may change between versions. +/// +/// See [`set_rhai_ahash_seed`] for more. +pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: Option<[u64; 4]> = None; + + AHASH_SEED + .get_or_init(|| FUNNY_YUMENIKKI_REFERENCE) + .map(|x| *x) + .flatten() +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 194ac6da..e2748d4a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,3 @@ //! Contains Configuration for Rhai. -pub mod hashing; \ No newline at end of file +pub mod hashing; From 80fef5a01cfab4067a85f487adc1c4a32bf8db1a Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Tue, 1 Nov 2022 22:42:35 +0900 Subject: [PATCH 091/116] remove static hash from CI --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc782151..cf7f1f7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: flags: - "" - "--features debugging" - - "--features metadata,serde,internals,static_hash" + - "--features metadata,serde,internals" - "--features unchecked,serde,metadata,internals,debugging" - "--features sync,serde,metadata,internals,debugging" - "--features no_position,serde,metadata,internals,debugging" @@ -129,7 +129,6 @@ jobs: matrix: flags: - "" - - "--features static_hash" include: - {os: ubuntu-latest, flags: "--profile unix", experimental: false} - {os: windows-latest, flags: "--profile windows", experimental: true} From 2a5ef0bee11157101829e88caa9a30992a9bae97 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 3 Nov 2022 12:00:56 +0800 Subject: [PATCH 092/116] Refactor is_anonymous. --- src/api/call_fn.rs | 13 +++++++------ src/func/call.rs | 12 ++++++++++-- src/func/mod.rs | 2 ++ src/func/native.rs | 8 ++++++-- src/types/fn_ptr.rs | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 8c0139e8..1207c522 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -259,17 +259,18 @@ impl Engine { ast.resolver().cloned(), ); - let mut result = Ok(Dynamic::UNIT); - - if eval_ast && !statements.is_empty() { - result = self.eval_global_statements(scope, global, caches, statements, lib, 0); + let result = if eval_ast && !statements.is_empty() { + let r = self.eval_global_statements(scope, global, caches, statements, lib, 0); if rewind_scope { scope.rewind(orig_scope_len); } - } - result = result.and_then(|_| { + r + } else { + Ok(Dynamic::UNIT) + } + .and_then(|_| { let mut args: StaticVec<_> = arg_values.iter_mut().collect(); // Check for data race. diff --git a/src/func/call.rs b/src/func/call.rs index 882060e9..22661345 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -4,8 +4,8 @@ use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn, CallableFunc use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; use crate::ast::{Expr, FnCallExpr, FnCallHashes}; use crate::engine::{ - KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, - KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, + FN_ANONYMOUS, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, + KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; @@ -127,6 +127,14 @@ pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) - Ok(()) } +/// Is a function name an anonymous function? +#[cfg(not(feature = "no_function"))] +#[inline] +#[must_use] +pub fn is_anonymous_fn(name: &str) -> bool { + name.starts_with(FN_ANONYMOUS) +} + impl Engine { /// Generate the signature for a function call. #[inline] diff --git a/src/func/mod.rs b/src/func/mod.rs index 0d886513..54652e74 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -15,6 +15,8 @@ pub use args::FuncArgs; pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; #[cfg(not(feature = "no_closure"))] pub use call::ensure_no_data_race; +#[cfg(not(feature = "no_function"))] +pub use call::is_anonymous_fn; pub use call::FnCallArgs; pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] diff --git a/src/func/native.rs b/src/func/native.rs index ba7f5ef8..70c84d11 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -3,8 +3,9 @@ use super::call::FnCallArgs; use crate::ast::FnCallHashes; use crate::eval::{Caches, GlobalRuntimeState}; +use crate::parser::is_anonymous_fn; use crate::plugin::PluginFunction; -use crate::tokenizer::{Token, TokenizeState}; +use crate::tokenizer::{is_valid_function_name, Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult, @@ -329,7 +330,10 @@ impl<'a> NativeCallContext<'a> { is_method_call: bool, args: &mut [&mut Dynamic], ) -> RhaiResult { - self._call_fn_raw(fn_name, false, is_ref_mut, is_method_call, args) + let name = fn_name.as_ref(); + let native_only = !is_valid_function_name(name) && !is_anonymous_fn(name); + + self._call_fn_raw(fn_name, native_only, is_ref_mut, is_method_call, args) } /// Call a registered native Rust function inside the call context. /// diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 2c2bd7d2..5938b7a6 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -106,7 +106,7 @@ impl FnPtr { #[inline(always)] #[must_use] pub fn is_anonymous(&self) -> bool { - self.name.starts_with(crate::engine::FN_ANONYMOUS) + crate::func::is_anonymous_fn(&self.name) } /// Call the function pointer with curried arguments (if any). /// The function may be script-defined (not available under `no_function`) or native Rust. From 36edc445d1afc79493092506badbc2fa713df619 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Fri, 4 Nov 2022 15:36:18 +0900 Subject: [PATCH 093/116] fix build.yml, concurrency concerns, build.rs concerns --- .github/workflows/build.yml | 31 ---------- build.rs | 6 +- build.template | 114 +----------------------------------- src/config/hashing.rs | 109 ++++++++++++++++++++++++++++------ src/config/hashing_env.rs | 2 + src/config/mod.rs | 1 + 6 files changed, 98 insertions(+), 165 deletions(-) create mode 100644 src/config/hashing_env.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf7f1f7b..d186abb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,37 +89,6 @@ jobs: command: test args: ${{matrix.flags}} -# nightly_features_build: -# name: NightlyFeatureBuild -# runs-on: ${{matrix.os}} -# continue-on-error: ${{matrix.experimental}} -# strategy: -# matrix: -# os: [ubuntu-latest] -# flags: -# - "--features stable_hash" -# toolchain: [ nightly ] -# experimental: [ false ] -# include: -# # smoketests for different toolchains -# - { toolchain: nightly, os: windows-latest, experimental: false, flags: "" } -# - { toolchain: nightly, os: macos-latest, experimental: false, flags: "" } -# fail-fast: false -# steps: -# - name: Checkout -# uses: actions/checkout@v2 -# - name: Setup Toolchain -# uses: actions-rs/toolchain@v1 -# with: -# toolchain: ${{matrix.toolchain}} -# override: true -# - name: Test -# uses: actions-rs/cargo@v1 -# with: -# command: test -# args: ${{matrix.flags}} - - # no-std builds are a bit more extensive to test no_std_build: name: NoStdBuild diff --git a/build.rs b/build.rs index e95b1dc3..39c11238 100644 --- a/build.rs +++ b/build.rs @@ -17,10 +17,10 @@ fn main() { let seed = env::var("RHAI_AHASH_SEED").map_or_else(|_| "None".into(), |s| format!("Some({s})")); - contents = contents.replace("{{ AHASH_SEED }}", &seed); + contents = contents.replace("{{AHASH_SEED}}", &seed); - File::create("src/config/hashing.rs") + File::create("src/config/hashing_env.rs") .expect("cannot create `config.rs`") .write_all(contents.as_bytes()) - .expect("cannot write to `config/hashing.rs`"); + .expect("cannot write to `config/hashing_env.rs`"); } diff --git a/build.template b/build.template index 89b0e403..a165e5ed 100644 --- a/build.template +++ b/build.template @@ -1,112 +1,2 @@ -//! Fixed hashing seeds for stable hashing. -//! -//! Set to [`None`] to disable stable hashing. -//! -//! See [`set_rhai_ahash_seed`] for more. -//! -//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` -//! environment variable instead. -//! -//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` -// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 - -use core::{ - cell::UnsafeCell, - marker::PhantomData, - mem::MaybeUninit, - panic::{RefUnwindSafe, UnwindSafe}, - sync::atomic::{AtomicBool, Ordering}, -}; - -// Safety: lol -struct SusLock -where - T: 'static, -{ - initalized: AtomicBool, - data: UnsafeCell>, - _marker: PhantomData, -} - -impl SusLock { - pub const fn new() -> SusLock { - SusLock { - initalized: AtomicBool::new(false), - data: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData, - } - } - - pub fn get(&self) -> Option<&T> { - if self.initalized.load(Ordering::SeqCst) { - Some(unsafe { (&*self.data.get()).assume_init_ref() }) - } else { - return None; - } - } - - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { - let value = f(); - if !self.initalized.load(Ordering::SeqCst) { - unsafe { - self.data.get().write(MaybeUninit::new(value)); - } - self.initalized.store(true, Ordering::SeqCst); - } - - self.get() - } - - pub fn set(&self, value: T) -> Result<(), T> { - if self.initalized.load(Ordering::SeqCst) { - Err(value) - } else { - let _ = self.get_or_init(|| value); - Ok(()) - } - } -} - -unsafe impl Sync for SusLock {} -unsafe impl Send for SusLock {} -impl RefUnwindSafe for SusLock {} - -impl Drop for SusLock { - fn drop(&mut self) { - if self.initalized.load(Ordering::SeqCst) { - unsafe { (&mut *self.data.get()).assume_init_drop() }; - } - } -} - -static AHASH_SEED: SusLock> = SusLock::new(); - -// #[doc(cfg(feature = "stable_hash"))] -/// Sets the Rhai Ahash seed. This is used to hash functions and the like. -/// -/// This is a global variable, and thus will affect every Rhai instance. -/// This should not be used _unless_ you know you need it. -/// -/// # Warnings -/// - You can only call this function **ONCE** for the whole of your program execution. -/// - You should gracefully handle the `Err(())`. -/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). -/// -/// # Errors -/// This will error if the AHashSeed is already set. -pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) -} - -/// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. -/// The default seed is not stable and may change between versions. -/// -/// See [`set_rhai_ahash_seed`] for more. -pub fn get_ahash_seed() -> Option<[u64; 4]> { - const FUNNY_YUMENIKKI_REFERENCE: Option<[u64; 4]> = {{ AHASH_SEED }}; - - AHASH_SEED - .get_or_init(|| FUNNY_YUMENIKKI_REFERENCE) - .map(|x| *x) - .flatten() -} +// This file is automatically set during build time by build.rs and build.template. +pub(crate) const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 3f318fef..2355a405 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -4,31 +4,94 @@ //! //! See [`set_rhai_ahash_seed`] for more. //! -//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +//! Alternatively, You can also set this at compile time by setting the `RHAI_AHASH_SEED` //! environment variable instead. //! //! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 +use crate::config::hashing_env; use core::{ cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, panic::{RefUnwindSafe, UnwindSafe}, - sync::atomic::{AtomicBool, Ordering}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; -// Safety: lol +// omg its hokma from record team here to record our locks +// what does this do? +// so what this does is keep track of a global address in memory that acts as a global lock +// i stole this from crossbeam so read their docs for more +struct HokmaLock { + lock: AtomicUsize, +} + +impl HokmaLock { + pub const fn new() -> Self { + Self { + lock: AtomicUsize::new(0), + } + } + + pub fn write(&'static self) -> WhenTheHokmaSupression { + loop { + let previous = self.lock.swap(1, Ordering::SeqCst); + + if previous != 1 { + return WhenTheHokmaSupression { + hokma: self, + state: previous, + }; + } + } + } +} + +struct WhenTheHokmaSupression { + hokma: &'static HokmaLock, + + state: usize, +} + +impl WhenTheHokmaSupression { + pub fn the_price_of_silence(self) { + self.hokma.lock.store(self.state, Ordering::SeqCst); + + core::mem::forget(self) + } +} + +impl Drop for WhenTheHokmaSupression { + fn drop(&mut self) { + self.hokma + .lock + .store(self.state.wrapping_add(2), Ordering::SeqCst) + } +} + +fn hokmalock(address: usize) -> &'static HokmaLock { + const LEN: usize = 787; + const LCK: HokmaLock = HokmaLock::new(); + static RECORDS: [HokmaLock; LEN] = [LCK; LEN]; + + &RECORDS[address % LEN] +} + +// Safety: lol, there is a reason its called "SusLock" struct SusLock where - T: 'static, + T: 'static + Copy, { initalized: AtomicBool, data: UnsafeCell>, _marker: PhantomData, } -impl SusLock { +impl SusLock +where + T: 'static + Copy, +{ pub const fn new() -> SusLock { SusLock { initalized: AtomicBool::new(false), @@ -37,21 +100,31 @@ impl SusLock { } } - pub fn get(&self) -> Option<&T> { + pub fn get(&self) -> Option { if self.initalized.load(Ordering::SeqCst) { - Some(unsafe { (&*self.data.get()).assume_init_ref() }) + let hokma = hokmalock(unsafe { core::mem::transmute(self.data.get()) }); + // we forgo the optimistic read, because we don't really care + let guard = hokma.write(); + let val = { + let cast: *const T = self.data.get().cast(); + unsafe { cast.read() } + }; + guard.the_price_of_silence(); + Some(val) } else { return None; } } - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option { let value = f(); if !self.initalized.load(Ordering::SeqCst) { + self.initalized.store(true, Ordering::SeqCst); + let hokma = hokmalock(unsafe { core::mem::transmute(self.data.get()) }); + hokma.write(); unsafe { self.data.get().write(MaybeUninit::new(value)); } - self.initalized.store(true, Ordering::SeqCst); } self.get() @@ -67,11 +140,14 @@ impl SusLock { } } -unsafe impl Sync for SusLock {} -unsafe impl Send for SusLock {} -impl RefUnwindSafe for SusLock {} +unsafe impl Sync for SusLock where T: 'static + Copy {} +unsafe impl Send for SusLock where T: 'static + Copy {} +impl RefUnwindSafe for SusLock where T: 'static + Copy {} -impl Drop for SusLock { +impl Drop for SusLock +where + T: 'static + Copy, +{ fn drop(&mut self) { if self.initalized.load(Ordering::SeqCst) { unsafe { (&mut *self.data.get()).assume_init_drop() }; @@ -103,10 +179,5 @@ pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]> /// /// See [`set_rhai_ahash_seed`] for more. pub fn get_ahash_seed() -> Option<[u64; 4]> { - const FUNNY_YUMENIKKI_REFERENCE: Option<[u64; 4]> = None; - - AHASH_SEED - .get_or_init(|| FUNNY_YUMENIKKI_REFERENCE) - .map(|x| *x) - .flatten() + AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED).flatten() } diff --git a/src/config/hashing_env.rs b/src/config/hashing_env.rs new file mode 100644 index 00000000..ee2b35db --- /dev/null +++ b/src/config/hashing_env.rs @@ -0,0 +1,2 @@ +// This file is automatically set during build time by build.rs and build.template. +pub(crate) const AHASH_SEED: Option<[u64; 4]> = None; diff --git a/src/config/mod.rs b/src/config/mod.rs index e2748d4a..db5448f6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,4 @@ //! Contains Configuration for Rhai. pub mod hashing; +mod hashing_env; From 8904b19015c23d041a7f9d6d1c0ab73b9e069eb8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 19:18:56 +0800 Subject: [PATCH 094/116] Fix data size. --- src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests.rs b/src/tests.rs index 34186402..8bbef0d4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -41,7 +41,7 @@ fn check_struct_sizes() { size_of::(), if cfg!(feature = "no_position") { 8 } else { 16 } ); - assert_eq!(size_of::(), 72); + assert_eq!(size_of::(), 64); assert_eq!( size_of::(), if cfg!(feature = "no_position") { From d2cd70bf53710a8f7feaeb5f22dedb10b2df993f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 21:22:31 +0800 Subject: [PATCH 095/116] Minor stylistic fixes. --- CHANGELOG.md | 6 +-- build.template | 3 +- src/config/hashing.rs | 104 ++++++++++++++++++++++++++------------ src/config/hashing_env.rs | 3 +- src/config/mod.rs | 2 +- 5 files changed, 79 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6f6d5c7..f73ee408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,11 +26,11 @@ New features * Normal loops return `()` as the value. * Loop expressions can be enabled/disabled via `Engine::set_allow_loop_expressions` -### Stable hashing +### Static hashing -* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. +* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via a static function `rhai::config::hashing::set_ahash_seed` or an environment variable, in order to force static (i.e. deterministic) hashes for function signatures. * This is necessary when using Rhai across shared-library boundaries. -* A build script is used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation. +* A build script is used to extract the environment variable (`RHAI_AHASH_SEED`, if any) and splice it into the source code before compilation. ### No Timestamps diff --git a/build.template b/build.template index a165e5ed..bce322e8 100644 --- a/build.template +++ b/build.template @@ -1,2 +1,3 @@ -// This file is automatically set during build time by build.rs and build.template. +//! This file is automatically recreated during build time by `build.rs` from `build.template`. + pub(crate) const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 2355a405..58374d21 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -2,18 +2,30 @@ //! //! Set to [`None`] to disable stable hashing. //! -//! See [`set_rhai_ahash_seed`] for more. +//! See [`set_rhai_ahash_seed`]. //! -//! Alternatively, You can also set this at compile time by setting the `RHAI_AHASH_SEED` -//! environment variable instead. +//! # Example //! -//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +//! ```rust +//! // Set the hashing seed to [1, 2, 3, 4] +//! rhai::config::hashing::set_ahash_seed(Some([1, 2, 3, 4])).unwrap(); +//! ``` +//! Alternatively, set this at compile time via the `RHAI_AHASH_SEED` environment variable. +//! +//! # Example +//! +//! ```sh +//! env RHAI_AHASH_SEED ="[236,800,954,213]" +//! ``` // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 use crate::config::hashing_env; -use core::{ +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ cell::UnsafeCell, marker::PhantomData, + mem, mem::MaybeUninit, panic::{RefUnwindSafe, UnwindSafe}, sync::atomic::{AtomicBool, AtomicUsize, Ordering}, @@ -23,23 +35,25 @@ use core::{ // what does this do? // so what this does is keep track of a global address in memory that acts as a global lock // i stole this from crossbeam so read their docs for more +#[must_use] struct HokmaLock { lock: AtomicUsize, } impl HokmaLock { + #[inline(always)] pub const fn new() -> Self { Self { lock: AtomicUsize::new(0), } } - pub fn write(&'static self) -> WhenTheHokmaSupression { + pub fn write(&'static self) -> WhenTheHokmaSuppression { loop { let previous = self.lock.swap(1, Ordering::SeqCst); if previous != 1 { - return WhenTheHokmaSupression { + return WhenTheHokmaSuppression { hokma: self, state: previous, }; @@ -48,21 +62,21 @@ impl HokmaLock { } } -struct WhenTheHokmaSupression { +struct WhenTheHokmaSuppression { hokma: &'static HokmaLock, - state: usize, } -impl WhenTheHokmaSupression { +impl WhenTheHokmaSuppression { + #[inline] pub fn the_price_of_silence(self) { self.hokma.lock.store(self.state, Ordering::SeqCst); - - core::mem::forget(self) + mem::forget(self) } } -impl Drop for WhenTheHokmaSupression { +impl Drop for WhenTheHokmaSuppression { + #[inline] fn drop(&mut self) { self.hokma .lock @@ -70,6 +84,8 @@ impl Drop for WhenTheHokmaSupression { } } +#[inline(always)] +#[must_use] fn hokmalock(address: usize) -> &'static HokmaLock { const LEN: usize = 787; const LCK: HokmaLock = HokmaLock::new(); @@ -79,11 +95,12 @@ fn hokmalock(address: usize) -> &'static HokmaLock { } // Safety: lol, there is a reason its called "SusLock" +#[must_use] struct SusLock where T: 'static + Copy, { - initalized: AtomicBool, + initialized: AtomicBool, data: UnsafeCell>, _marker: PhantomData, } @@ -92,17 +109,19 @@ impl SusLock where T: 'static + Copy, { + #[inline] pub const fn new() -> SusLock { SusLock { - initalized: AtomicBool::new(false), + initialized: AtomicBool::new(false), data: UnsafeCell::new(MaybeUninit::uninit()), _marker: PhantomData, } } + #[must_use] pub fn get(&self) -> Option { - if self.initalized.load(Ordering::SeqCst) { - let hokma = hokmalock(unsafe { core::mem::transmute(self.data.get()) }); + if self.initialized.load(Ordering::SeqCst) { + let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); // we forgo the optimistic read, because we don't really care let guard = hokma.write(); let val = { @@ -116,11 +135,12 @@ where } } + #[must_use] pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option { let value = f(); - if !self.initalized.load(Ordering::SeqCst) { - self.initalized.store(true, Ordering::SeqCst); - let hokma = hokmalock(unsafe { core::mem::transmute(self.data.get()) }); + if !self.initialized.load(Ordering::SeqCst) { + self.initialized.store(true, Ordering::SeqCst); + let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); hokma.write(); unsafe { self.data.get().write(MaybeUninit::new(value)); @@ -131,7 +151,7 @@ where } pub fn set(&self, value: T) -> Result<(), T> { - if self.initalized.load(Ordering::SeqCst) { + if self.initialized.load(Ordering::SeqCst) { Err(value) } else { let _ = self.get_or_init(|| value); @@ -148,8 +168,9 @@ impl Drop for SusLock where T: 'static + Copy, { + #[inline] fn drop(&mut self) { - if self.initalized.load(Ordering::SeqCst) { + if self.initialized.load(Ordering::SeqCst) { unsafe { (&mut *self.data.get()).assume_init_drop() }; } } @@ -157,27 +178,44 @@ where static AHASH_SEED: SusLock> = SusLock::new(); -// #[doc(cfg(feature = "stable_hash"))] -/// Sets the Rhai Ahash seed. This is used to hash functions and the like. +/// Set the hashing seed. This is used to hash functions etc. /// -/// This is a global variable, and thus will affect every Rhai instance. +/// This is a static global value and affects every Rhai instance. /// This should not be used _unless_ you know you need it. /// -/// # Warnings -/// - You can only call this function **ONCE** for the whole of your program execution. -/// - You should gracefully handle the `Err(())`. -/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +/// # Warning /// -/// # Errors -/// This will error if the AHashSeed is already set. +/// * You can only call this function **ONCE** for the entire duration of program execution. +/// * You **MUST** call this before performing **ANY** Rhai operation (e.g. creating an [`Engine`]). +/// +/// # Error +/// +/// Returns an error containing the existing hashing seed if already set. +/// +/// # Example +/// +/// ```rust +/// # use rhai::Engine; +/// // Set the hashing seed to [1, 2, 3, 4] +/// rhai::config::hashing::set_ahash_seed(Some([1, 2, 3, 4])).unwrap(); +/// +/// // Use Rhai AFTER setting the hashing seed +/// let engine = Engine::new(); +/// ``` +#[inline(always)] pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { AHASH_SEED.set(new_seed) } -/// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. -/// The default seed is not stable and may change between versions. +/// Get the current hashing Seed. +/// +/// If the seed is not yet defined, the `RHAI_AHASH_SEED` environment variable (if any) is used. +/// +/// Otherwise, the hashing seed is randomized to protect against DOS attacks. /// /// See [`set_rhai_ahash_seed`] for more. +#[inline] +#[must_use] pub fn get_ahash_seed() -> Option<[u64; 4]> { AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED).flatten() } diff --git a/src/config/hashing_env.rs b/src/config/hashing_env.rs index ee2b35db..59930ad8 100644 --- a/src/config/hashing_env.rs +++ b/src/config/hashing_env.rs @@ -1,2 +1,3 @@ -// This file is automatically set during build time by build.rs and build.template. +//! This file is automatically recreated during build time by `build.rs` from `build.template`. + pub(crate) const AHASH_SEED: Option<[u64; 4]> = None; diff --git a/src/config/mod.rs b/src/config/mod.rs index db5448f6..35aac0af 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,4 +1,4 @@ -//! Contains Configuration for Rhai. +//! Configuration for Rhai. pub mod hashing; mod hashing_env; From d80555e20490bf4ee9feb6631fcb44736bc3364c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 21:28:04 +0800 Subject: [PATCH 096/116] Fix no_function build. --- src/func/call.rs | 6 +++--- src/func/native.rs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index 22661345..da03b7c9 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -4,8 +4,8 @@ use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn, CallableFunc use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; use crate::ast::{Expr, FnCallExpr, FnCallHashes}; use crate::engine::{ - FN_ANONYMOUS, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, - KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, + KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, + KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; @@ -132,7 +132,7 @@ pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) - #[inline] #[must_use] pub fn is_anonymous_fn(name: &str) -> bool { - name.starts_with(FN_ANONYMOUS) + name.starts_with(crate::engine::FN_ANONYMOUS) } impl Engine { diff --git a/src/func/native.rs b/src/func/native.rs index 70c84d11..654db479 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -3,7 +3,6 @@ use super::call::FnCallArgs; use crate::ast::FnCallHashes; use crate::eval::{Caches, GlobalRuntimeState}; -use crate::parser::is_anonymous_fn; use crate::plugin::PluginFunction; use crate::tokenizer::{is_valid_function_name, Token, TokenizeState}; use crate::types::dynamic::Variant; @@ -331,7 +330,7 @@ impl<'a> NativeCallContext<'a> { args: &mut [&mut Dynamic], ) -> RhaiResult { let name = fn_name.as_ref(); - let native_only = !is_valid_function_name(name) && !is_anonymous_fn(name); + let native_only = !is_valid_function_name(name) && !crate::parser::is_anonymous_fn(name); self._call_fn_raw(fn_name, native_only, is_ref_mut, is_method_call, args) } From 0756994038c3f6fb27e222b70d46a3f9b9d1b751 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 21:42:57 +0800 Subject: [PATCH 097/116] Fix no_function. --- src/func/native.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/func/native.rs b/src/func/native.rs index 654db479..9d44125c 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -330,7 +330,9 @@ impl<'a> NativeCallContext<'a> { args: &mut [&mut Dynamic], ) -> RhaiResult { let name = fn_name.as_ref(); - let native_only = !is_valid_function_name(name) && !crate::parser::is_anonymous_fn(name); + let native_only = !is_valid_function_name(name); + #[cfg(not(feature = "no_function"))] + let native_only = native_only && !crate::parser::is_anonymous_fn(name); self._call_fn_raw(fn_name, native_only, is_ref_mut, is_method_call, args) } From 35b02ce9b74591d20904f82e9bf49085d670c538 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 21:47:09 +0800 Subject: [PATCH 098/116] Make parameters order uniform. --- src/api/call_fn.rs | 8 +- src/api/eval.rs | 20 ++--- src/api/run.rs | 4 +- src/eval/chaining.rs | 171 ++++++++++++++++++++------------------- src/eval/debugger.rs | 31 ++++--- src/eval/eval_context.rs | 12 +-- src/eval/expr.rs | 60 +++++++------- src/eval/stmt.rs | 92 ++++++++++----------- src/func/call.rs | 120 +++++++++++++-------------- src/func/native.rs | 6 +- src/func/script.rs | 14 ++-- src/module/mod.rs | 5 +- src/optimizer.rs | 2 +- src/parser.rs | 4 +- 14 files changed, 284 insertions(+), 265 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 1207c522..1a9e2347 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -260,7 +260,7 @@ impl Engine { ); let result = if eval_ast && !statements.is_empty() { - let r = self.eval_global_statements(scope, global, caches, statements, lib, 0); + let r = self.eval_global_statements(global, caches, lib, 0, scope, statements); if rewind_scope { scope.rewind(orig_scope_len); @@ -279,16 +279,16 @@ impl Engine { if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) { self.call_script_fn( - scope, global, caches, lib, + 0, + scope, &mut this_ptr, fn_def, &mut args, rewind_scope, Position::NONE, - 0, ) } else { Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()) @@ -306,7 +306,7 @@ impl Engine { if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(scope, global, lib, &mut this_ptr, node, 0)?; + self.run_debugger(global, caches, lib, 0, scope, &mut this_ptr, node)?; } Ok(result) diff --git a/src/api/eval.rs b/src/api/eval.rs index 3f37264d..ffef8623 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -186,8 +186,9 @@ impl Engine { ast: &AST, ) -> RhaiResultOf { let global = &mut GlobalRuntimeState::new(self); + let caches = &mut Caches::new(); - let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?; + let result = self.eval_ast_with_scope_raw(global, caches, 0, scope, ast)?; #[cfg(feature = "debugging")] if self.debugger.is_some() { @@ -197,7 +198,7 @@ impl Engine { ast.as_ref(), ]; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(scope, global, lib, &mut None, node, 0)?; + self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; } let typ = self.map_type_name(result.type_name()); @@ -211,12 +212,12 @@ impl Engine { #[inline] pub(crate) fn eval_ast_with_scope_raw<'a>( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, - ast: &'a AST, + caches: &mut Caches, level: usize, + scope: &mut Scope, + ast: &'a AST, ) -> RhaiResult { - let mut caches = Caches::new(); global.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] @@ -240,8 +241,7 @@ impl Engine { _lib = &[]; } - let result = - self.eval_global_statements(scope, global, &mut caches, statements, _lib, level); + let result = self.eval_global_statements(global, caches, _lib, level, scope, statements); #[cfg(not(feature = "no_module"))] { @@ -262,14 +262,14 @@ impl Engine { #[inline(always)] pub fn eval_statements_raw( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, - statements: &[crate::ast::Stmt], lib: &[&crate::Module], level: usize, + scope: &mut Scope, + statements: &[crate::ast::Stmt], ) -> RhaiResult { - self.eval_global_statements(scope, global, caches, statements, lib, level) + self.eval_global_statements(global, caches, lib, level, scope, statements) } } diff --git a/src/api/run.rs b/src/api/run.rs index e32136e6..effdd4b6 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -131,7 +131,7 @@ impl Engine { } else { &lib }; - self.eval_global_statements(scope, global, caches, statements, lib, 0)?; + self.eval_global_statements(global, caches, lib, 0, scope, statements)?; } #[cfg(feature = "debugging")] @@ -142,7 +142,7 @@ impl Engine { ast.as_ref(), ]; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(scope, global, lib, &mut None, node, 0)?; + self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index e7e1e754..ecc40fc7 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -41,6 +41,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, root: (&str, Position), @@ -49,7 +50,6 @@ impl Engine { rhs: &Expr, idx_values: &mut FnArgsVec, chain_type: ChainType, - level: usize, new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); @@ -73,7 +73,7 @@ impl Engine { if !parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); let mut idx_val_for_setter = idx_val.clone(); @@ -82,14 +82,14 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - global, caches, lib, target, idx_val, idx_pos, false, true, level, + global, caches, lib, level, target, idx_val, idx_pos, false, true, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, obj_ptr, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, level, new_val, + global, caches, lib, level, this_ptr, obj_ptr, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, new_val, ) { Ok((result, true)) if is_obj_temp_val => { (Some(obj.take_or_clone()), (result, true)) @@ -104,7 +104,7 @@ impl Engine { let idx = &mut idx_val_for_setter; let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, target, idx, new_val, is_ref_mut, level, + global, caches, lib, level, target, idx, new_val, is_ref_mut, ) .or_else(|e| match *e { ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), @@ -117,19 +117,19 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; let (new_val, op_info) = new_val.take().expect("`Some`"); let idx_val = &mut idx_values.pop().unwrap(); let idx = &mut idx_val.clone(); let try_setter = match self.get_indexed_mut( - global, caches, lib, target, idx, pos, true, false, level, + global, caches, lib, level, target, idx, pos, true, false, ) { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - global, caches, lib, op_info, obj_ptr, root, new_val, level, + global, caches, lib, level, op_info, obj_ptr, root, new_val, )?; self.check_data_size(obj_ptr, op_info.pos)?; None @@ -148,13 +148,13 @@ impl Engine { // Call the index getter to get the current value if let Ok(val) = - self.call_indexer_get(global, caches, lib, target, idx, level) + self.call_indexer_get(global, caches, lib, level, target, idx) { let mut val = val.into(); // Run the op-assignment self.eval_op_assignment( - global, caches, lib, op_info, &mut val, root, new_val, - level, + global, caches, lib, level, op_info, &mut val, root, + new_val, )?; // Replace new value new_val = val.take_or_clone(); @@ -166,7 +166,7 @@ impl Engine { let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, target, idx_val, new_val, is_ref_mut, level, + global, caches, lib, level, target, idx_val, new_val, is_ref_mut, )?; } @@ -175,12 +175,12 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, _parent, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); self.get_indexed_mut( - global, caches, lib, target, idx_val, pos, false, true, level, + global, caches, lib, level, target, idx_val, pos, false, true, ) .map(|v| (v.take_or_clone(), false)) } @@ -198,8 +198,9 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset_debugger = - self.run_debugger_with_reset(scope, global, lib, this_ptr, rhs, level)?; + let reset_debugger = self.run_debugger_with_reset( + global, caches, lib, level, scope, this_ptr, rhs, + )?; let crate::ast::FnCallExpr { name, hashes, args, .. @@ -210,8 +211,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); let result = self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos1, *pos, - level, + global, caches, lib, level, name, *hashes, target, call_args, pos1, + *pos, ); idx_values.truncate(offset); @@ -232,16 +233,16 @@ impl Engine { // {xxx:map}.id op= ??? Expr::Property(x, pos) if target.is::() && new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let (new_val, op_info) = new_val.take().expect("`Some`"); { let val_target = &mut self.get_indexed_mut( - global, caches, lib, target, index, *pos, true, false, level, + global, caches, lib, level, target, index, *pos, true, false, )?; self.eval_op_assignment( - global, caches, lib, op_info, val_target, root, new_val, level, + global, caches, lib, level, op_info, val_target, root, new_val, )?; } self.check_data_size(target.source(), op_info.pos)?; @@ -250,18 +251,18 @@ impl Engine { // {xxx:map}.id Expr::Property(x, pos) if target.is::() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let val = self.get_indexed_mut( - global, caches, lib, target, index, *pos, false, false, level, + global, caches, lib, level, target, index, *pos, false, false, )?; Ok((val.take_or_clone(), false)) } // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; let ((getter, hash_get), (setter, hash_set), name) = &**x; let (mut new_val, op_info) = new_val.take().expect("`Some`"); @@ -270,15 +271,15 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, lib, getter, None, *hash_get, args, is_ref_mut, - *pos, level, + global, caches, lib, level, getter, None, *hash_get, args, + is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, target, &mut prop, level, + global, caches, lib, level, target, &mut prop, ) .map(|r| (r, false)) .map_err(|e| { @@ -295,7 +296,7 @@ impl Engine { let orig_val = &mut (&mut orig_val).into(); self.eval_op_assignment( - global, caches, lib, op_info, orig_val, root, new_val, level, + global, caches, lib, level, op_info, orig_val, root, new_val, )?; } @@ -304,8 +305,8 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos, - level, + global, caches, lib, level, setter, None, *hash_set, args, is_ref_mut, + *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -313,7 +314,7 @@ impl Engine { let idx = &mut name.into(); let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, target, idx, new_val, is_ref_mut, level, + global, caches, lib, level, target, idx, new_val, is_ref_mut, ) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -326,13 +327,13 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, rhs, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos, - level, + global, caches, lib, level, getter, None, *hash_get, args, is_ref_mut, + *pos, ) .map_or_else( |err| match *err { @@ -340,7 +341,7 @@ impl Engine { ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, target, &mut prop, level, + global, caches, lib, level, target, &mut prop, ) .map(|r| (r, false)) .map_err(|e| match *e { @@ -363,18 +364,20 @@ impl Engine { let val_target = &mut match x.lhs { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, _node, level)?; + self.run_debugger( + global, caches, lib, level, scope, this_ptr, _node, + )?; let index = &mut p.2.clone().into(); self.get_indexed_mut( - global, caches, lib, target, index, pos, false, true, level, + global, caches, lib, level, target, index, pos, false, true, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset( - scope, global, lib, this_ptr, _node, level, + global, caches, lib, level, scope, this_ptr, _node, )?; let crate::ast::FnCallExpr { @@ -386,8 +389,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); let result = self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos1, - pos, level, + global, caches, lib, level, name, *hashes, target, call_args, + pos1, pos, ); idx_values.truncate(offset); @@ -407,8 +410,8 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs, - idx_values, rhs_chain, level, new_val, + global, caches, lib, level, this_ptr, val_target, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } @@ -420,7 +423,9 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, _node, level)?; + self.run_debugger( + global, caches, lib, level, scope, this_ptr, _node, + )?; let ((getter, hash_get), (setter, hash_set), name) = &**p; let rhs_chain = rhs.into(); @@ -430,15 +435,15 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, lib, getter, None, *hash_get, args, - is_ref_mut, pos, level, + global, caches, lib, level, getter, None, *hash_get, args, + is_ref_mut, pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, target, &mut prop, level, + global, caches, lib, level, target, &mut prop, ) .map(|r| (r, false)) .map_err( @@ -455,8 +460,8 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, level, new_val, + global, caches, lib, level, this_ptr, val, root, rhs, + *options, &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -466,8 +471,8 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_native_fn_call( - global, caches, lib, setter, None, *hash_set, args, - is_ref_mut, pos, level, + global, caches, lib, level, setter, None, *hash_set, args, + is_ref_mut, pos, ) .or_else( |err| match *err { @@ -476,8 +481,8 @@ impl Engine { let idx = &mut name.into(); let new_val = val; self.call_indexer_set( - global, caches, lib, target, idx, new_val, - is_ref_mut, level, + global, caches, lib, level, target, idx, + new_val, is_ref_mut, ) .or_else(|e| match *e { // If there is no setter, no need to feed it @@ -499,7 +504,7 @@ impl Engine { Expr::MethodCall(ref f, pos) if !f.is_qualified() => { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset( - scope, global, lib, this_ptr, _node, level, + global, caches, lib, level, scope, this_ptr, _node, )?; let crate::ast::FnCallExpr { @@ -512,8 +517,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); let result = self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos1, - pos, level, + global, caches, lib, level, name, *hashes, target, call_args, + pos1, pos, ); idx_values.truncate(offset); @@ -525,8 +530,8 @@ impl Engine { let val = &mut val.into(); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, level, new_val, + global, caches, lib, level, this_ptr, val, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(pos)) } @@ -548,13 +553,13 @@ impl Engine { /// Evaluate a dot/index chain. pub(crate) fn eval_dot_index_chain( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, - level: usize, new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { let chain_type = ChainType::from(expr); @@ -592,8 +597,8 @@ impl Engine { // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( - scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, - level, + global, caches, lib, level, scope, this_ptr, rhs, options, chain_type, + idx_values, )?; } } @@ -602,18 +607,18 @@ impl Engine { // id.??? or id[???] Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, lhs, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, lhs)?; self.track_operation(global, *var_pos)?; let (mut target, ..) = - self.search_namespace(scope, global, lib, this_ptr, lhs, level)?; + self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); self.eval_dot_index_chain_helper( - global, caches, lib, &mut None, obj_ptr, root, expr, options, rhs, idx_values, - chain_type, level, new_val, + global, caches, lib, level, &mut None, obj_ptr, root, expr, options, rhs, + idx_values, chain_type, new_val, ) } // {expr}.??? = ??? or {expr}[???] = ??? @@ -621,14 +626,14 @@ impl Engine { // {expr}.??? or {expr}[???] expr => { let value = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level)? + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? .flatten(); let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, - chain_type, level, new_val, + global, caches, lib, level, this_ptr, obj_ptr, root, expr, options, rhs, + idx_values, chain_type, new_val, ) } } @@ -639,16 +644,16 @@ impl Engine { /// Evaluate a chain of indexes and store the results in a [`FnArgsVec`]. fn eval_dot_index_chain_arguments( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, parent_options: ASTFlags, parent_chain_type: ChainType, idx_values: &mut FnArgsVec, - level: usize, ) -> RhaiResultOf<()> { self.track_operation(global, expr.position())?; @@ -659,7 +664,7 @@ impl Engine { { for arg_expr in &x.args { idx_values.push( - self.get_arg_value(scope, global, caches, lib, this_ptr, arg_expr, level)? + self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg_expr)? .0 .flatten(), ); @@ -694,7 +699,7 @@ impl Engine { for arg_expr in &x.args { _arg_values.push( self.get_arg_value( - scope, global, caches, lib, this_ptr, arg_expr, level, + global, caches, lib, level, scope, this_ptr, arg_expr, )? .0 .flatten(), @@ -712,7 +717,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => { _arg_values.push( - self.eval_expr(scope, global, caches, lib, this_ptr, lhs, level)? + self.eval_expr(global, caches, lib, level, scope, this_ptr, lhs)? .flatten(), ); } @@ -723,8 +728,8 @@ impl Engine { let chain_type = expr.into(); self.eval_dot_index_chain_arguments( - scope, global, caches, lib, this_ptr, rhs, *options, chain_type, idx_values, - level, + global, caches, lib, level, scope, this_ptr, rhs, *options, chain_type, + idx_values, )?; if !_arg_values.is_empty() { @@ -738,7 +743,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)? + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? .flatten(), ), _ => unreachable!("unknown chained expression: {:?}", expr), @@ -754,9 +759,9 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, target: &mut Dynamic, idx: &mut Dynamic, - level: usize, ) -> RhaiResultOf { let args = &mut [target, idx]; let hash = global.hash_idx_get(); @@ -765,7 +770,7 @@ impl Engine { let level = level + 1; self.exec_native_fn_call( - global, caches, lib, fn_name, None, hash, args, true, pos, level, + global, caches, lib, level, fn_name, None, hash, args, true, pos, ) .map(|(r, ..)| r) } @@ -777,11 +782,11 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, target: &mut Dynamic, idx: &mut Dynamic, new_val: &mut Dynamic, is_ref_mut: bool, - level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { let hash = global.hash_idx_set(); let args = &mut [target, idx, new_val]; @@ -790,7 +795,7 @@ impl Engine { let level = level + 1; self.exec_native_fn_call( - global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos, level, + global, caches, lib, level, fn_name, None, hash, args, is_ref_mut, pos, ) } @@ -801,12 +806,12 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, idx_pos: Position, _add_if_not_found: bool, use_indexers: bool, - level: usize, ) -> RhaiResultOf> { self.track_operation(global, Position::NONE)?; @@ -1010,7 +1015,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, caches, lib, target, idx, level) + .call_indexer_get(global, caches, lib, level, target, idx) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 2ddf0991..9b516fdf 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -1,7 +1,7 @@ //! Module defining the debugging interface. #![cfg(feature = "debugging")] -use super::{EvalContext, GlobalRuntimeState}; +use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{ Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, @@ -411,16 +411,17 @@ impl Engine { #[inline(always)] pub(crate) fn run_debugger<'a>( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, node: impl Into>, - level: usize, ) -> RhaiResultOf<()> { if self.debugger.is_some() { if let Some(cmd) = - self.run_debugger_with_reset_raw(scope, global, lib, this_ptr, node, level)? + self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node)? { global.debugger.status = cmd; } @@ -437,15 +438,16 @@ impl Engine { #[inline(always)] pub(crate) fn run_debugger_with_reset<'a>( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, node: impl Into>, - level: usize, ) -> RhaiResultOf> { if self.debugger.is_some() { - self.run_debugger_with_reset_raw(scope, global, lib, this_ptr, node, level) + self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node) } else { Ok(None) } @@ -459,12 +461,13 @@ impl Engine { #[inline] pub(crate) fn run_debugger_with_reset_raw<'a>( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, node: impl Into>, - level: usize, ) -> RhaiResultOf> { let node = node.into(); @@ -494,7 +497,7 @@ impl Engine { }, }; - self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level) + self.run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) } /// Run the debugger callback unconditionally. /// @@ -505,17 +508,19 @@ impl Engine { #[inline] pub(crate) fn run_debugger_raw<'a>( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, node: ASTNode<'a>, event: DebuggerEvent, - level: usize, ) -> Result, Box> { 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); + let context = + crate::EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index b1185a39..172720cd 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -31,12 +31,12 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, ' #[must_use] pub fn new( engine: &'a Engine, - scope: &'s mut Scope<'ps>, global: &'g mut GlobalRuntimeState<'pg>, caches: Option<&'c mut Caches<'pc>>, lib: &'a [&'a Module], - this_ptr: &'t mut Option<&'pt mut Dynamic>, level: usize, + scope: &'s mut Scope<'ps>, + this_ptr: &'t mut Option<&'pt mut Dynamic>, ) -> Self { Self { engine, @@ -182,23 +182,23 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, ' match expr { crate::ast::Expr::Stmt(statements) => self.engine.eval_stmt_block( - self.scope, self.global, caches, self.lib, + self.level, + self.scope, self.this_ptr, statements, rewind_scope, - self.level, ), _ => self.engine.eval_expr( - self.scope, self.global, caches, self.lib, + self.level, + self.scope, self.this_ptr, expr, - self.level, ), } } diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 521cb036..35da7117 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -49,25 +49,26 @@ impl Engine { /// depending on whether the variable name is namespace-qualified. pub(crate) fn search_namespace<'s>( &self, - scope: &'s mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, - level: usize, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(_, Some(_), _) => { - self.search_scope_only(scope, global, lib, this_ptr, expr, level) + self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) } Expr::Variable(v, None, _var_pos) => match &**v { // Normal variable access #[cfg(not(feature = "no_module"))] (_, ns, ..) if ns.is_empty() => { - self.search_scope_only(scope, global, lib, this_ptr, expr, level) + self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) } #[cfg(feature = "no_module")] - (_, (), ..) => self.search_scope_only(scope, global, lib, this_ptr, expr, level), + (_, (), ..) => self.search_scope_only(scope, global, lib, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] @@ -132,12 +133,13 @@ impl Engine { /// Panics if `expr` is not [`Expr::Variable`]. pub(crate) fn search_scope_only<'s>( &self, - scope: &'s mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, - level: usize, ) -> RhaiResultOf<(Target<'s>, Position)> { // Make sure that the pointer indirection is taken only when absolutely necessary. @@ -169,7 +171,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, scope, global, None, lib, this_ptr, level); + let context = EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -215,13 +217,13 @@ impl Engine { // Errors that are not recoverable, such as system errors or safety errors, can use `?`. pub(crate) fn eval_expr( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, - level: usize, ) -> RhaiResult { // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -231,12 +233,12 @@ impl Engine { if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] let reset_debugger = - self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?; + self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; let result = - self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, *pos, level); + self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -249,7 +251,7 @@ impl Engine { // will cost more than the mis-predicted `match` branch. if let Expr::Variable(x, index, var_pos) = expr { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, expr, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; @@ -259,14 +261,14 @@ impl Engine { .cloned() .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) } else { - self.search_namespace(scope, global, lib, this_ptr, expr, level) + self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) }; } #[cfg(feature = "debugging")] let reset_debugger = - self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?; + self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; @@ -293,12 +295,12 @@ impl Engine { .iter() .try_for_each(|expr| { let item = - self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?; + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; op_info.pos = expr.start_position(); self.eval_op_assignment( - global, caches, lib, &op_info, target, root, item, level, + global, caches, lib, level, &op_info, target, root, item, ) }) .map(|_| concat.take_or_clone()); @@ -316,7 +318,7 @@ impl Engine { crate::Array::with_capacity(x.len()), |mut array, item_expr| { let value = self - .eval_expr(scope, global, caches, lib, this_ptr, item_expr, level)? + .eval_expr(global, caches, lib, level, scope, this_ptr, item_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -348,7 +350,7 @@ impl Engine { x.0.iter() .try_fold(x.1.clone(), |mut map, (key, value_expr)| { let value = self - .eval_expr(scope, global, caches, lib, this_ptr, value_expr, level)? + .eval_expr(global, caches, lib, level, scope, this_ptr, value_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -372,7 +374,7 @@ impl Engine { Expr::And(x, ..) => { let lhs = self - .eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, x.lhs.position()) @@ -381,7 +383,7 @@ impl Engine { match lhs { Ok(true) => self - .eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) .and_then(|v| { v.as_bool() .map_err(|typ| { @@ -395,7 +397,7 @@ impl Engine { Expr::Or(x, ..) => { let lhs = self - .eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, x.lhs.position()) @@ -404,7 +406,7 @@ impl Engine { match lhs { Ok(false) => self - .eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) .and_then(|v| { v.as_bool() .map_err(|typ| { @@ -417,11 +419,11 @@ impl Engine { } Expr::Coalesce(x, ..) => { - let lhs = self.eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level); + let lhs = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs); match lhs { Ok(value) if value.is::<()>() => { - self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) } _ => lhs, } @@ -442,7 +444,7 @@ impl Engine { )) })?; let mut context = - EvalContext::new(self, scope, global, Some(caches), lib, this_ptr, level); + EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); @@ -451,16 +453,16 @@ impl Engine { Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) => { - self.eval_stmt_block(scope, global, caches, lib, this_ptr, x, true, level) + self.eval_stmt_block(global, caches, lib, level, scope, this_ptr, x, true) } #[cfg(not(feature = "no_index"))] Expr::Index(..) => self - .eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, &mut None), + .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), #[cfg(not(feature = "no_object"))] Expr::Dot(..) => self - .eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, &mut None), + .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), _ => unreachable!("expression cannot be evaluated: {:?}", expr), }; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index f61749a8..5bf03093 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -25,14 +25,14 @@ impl Engine { // Errors that are not recoverable, such as system errors or safety errors, can use `?`. pub(crate) fn eval_stmt_block( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, statements: &[Stmt], restore_orig_state: bool, - level: usize, ) -> RhaiResult { if statements.is_empty() { return Ok(Dynamic::UNIT); @@ -53,14 +53,14 @@ impl Engine { let imports_len = global.num_imports(); let result = self.eval_stmt( - scope, global, caches, lib, + level, + scope, this_ptr, stmt, restore_orig_state, - level, )?; #[cfg(not(feature = "no_module"))] @@ -113,11 +113,11 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, op_info: &OpAssignment, target: &mut Target, root: (&str, Position), new_val: Dynamic, - level: usize, ) -> RhaiResultOf<()> { if target.is_read_only() { // Assignment to constant variable @@ -156,7 +156,7 @@ impl Engine { let token = Some(op_assign_token); match self.exec_native_fn_call( - global, caches, lib, op_assign, token, hash, args, true, *op_pos, level, + global, caches, lib, level, op_assign, token, hash, args, true, *op_pos, ) { Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => @@ -166,7 +166,7 @@ impl Engine { *args[0] = self .exec_native_fn_call( - global, caches, lib, op, token, *hash_op, args, true, *op_pos, level, + global, caches, lib, level, op, token, *hash_op, args, true, *op_pos, ) .map_err(|err| err.fill_position(op_info.pos))? .0 @@ -194,18 +194,18 @@ impl Engine { // Errors that are not recoverable, such as system errors or safety errors, can use `?`. pub(crate) fn eval_stmt( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, stmt: &Stmt, rewind_scope: bool, - level: usize, ) -> RhaiResult { #[cfg(feature = "debugging")] let reset_debugger = - self.run_debugger_with_reset(scope, global, lib, this_ptr, stmt, level)?; + self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -215,7 +215,7 @@ impl Engine { self.track_operation(global, stmt.position())?; let result = - self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, *pos, level); + self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -233,12 +233,12 @@ impl Engine { let result = if let Expr::Variable(x, ..) = lhs { let rhs_result = self - .eval_expr(scope, global, caches, lib, this_ptr, rhs, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, rhs) .map(Dynamic::flatten); if let Ok(rhs_val) = rhs_result { let search_result = - self.search_namespace(scope, global, lib, this_ptr, lhs, level); + self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs); if let Ok(search_val) = search_result { let (mut lhs_ptr, pos) = search_val; @@ -265,7 +265,7 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; self.eval_op_assignment( - global, caches, lib, op_info, lhs_ptr, root, rhs_val, level, + global, caches, lib, level, op_info, lhs_ptr, root, rhs_val, ) .map(|_| Dynamic::UNIT) } else { @@ -277,7 +277,7 @@ impl Engine { } else { let (op_info, BinaryExpr { lhs, rhs }) = &**x; - let rhs_result = self.eval_expr(scope, global, caches, lib, this_ptr, rhs, level); + let rhs_result = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs); if let Ok(rhs_val) = rhs_result { // Check if the result is a string. If so, intern it. @@ -307,14 +307,14 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Index(..) => self .eval_dot_index_chain( - scope, global, caches, lib, this_ptr, lhs, level, _new_val, + global, caches, lib, level, scope, this_ptr, lhs, _new_val, ) .map(|_| Dynamic::UNIT), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] Expr::Dot(..) => self .eval_dot_index_chain( - scope, global, caches, lib, this_ptr, lhs, level, _new_val, + global, caches, lib, level, scope, this_ptr, lhs, _new_val, ) .map(|_| Dynamic::UNIT), _ => unreachable!("cannot assign to expression: {:?}", lhs), @@ -338,13 +338,13 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .map(Dynamic::flatten), // Block scope Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT), Stmt::Block(statements, ..) => self.eval_stmt_block( - scope, global, caches, lib, this_ptr, statements, true, level, + global, caches, lib, level, scope, this_ptr, statements, true, ), // If statement @@ -352,7 +352,7 @@ impl Engine { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, expr.position()) @@ -362,11 +362,11 @@ impl Engine { match guard_val { Ok(true) if if_block.is_empty() => Ok(Dynamic::UNIT), Ok(true) => self.eval_stmt_block( - scope, global, caches, lib, this_ptr, if_block, true, level, + global, caches, lib, level, scope, this_ptr, if_block, true, ), Ok(false) if else_block.is_empty() => Ok(Dynamic::UNIT), Ok(false) => self.eval_stmt_block( - scope, global, caches, lib, this_ptr, else_block, true, level, + global, caches, lib, level, scope, this_ptr, else_block, true, ), err => err.map(Into::into), } @@ -385,7 +385,7 @@ impl Engine { ) = &**x; let value_result = - self.eval_expr(scope, global, caches, lib, this_ptr, expr, level); + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr); if let Ok(value) = value_result { let expr_result = if value.is_hashable() { @@ -405,7 +405,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => Ok(b), ref c => self - .eval_expr(scope, global, caches, lib, this_ptr, c, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, c) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::( @@ -436,7 +436,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => Ok(b), ref c => self - .eval_expr(scope, global, caches, lib, this_ptr, c, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, c) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::( @@ -466,12 +466,12 @@ impl Engine { }; if let Ok(Some(expr)) = expr_result { - self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) } else if let Ok(None) = expr_result { // Default match clause def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { let def_expr = &expressions[index].expr; - self.eval_expr(scope, global, caches, lib, this_ptr, def_expr, level) + self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) }) } else { expr_result.map(|_| Dynamic::UNIT) @@ -492,7 +492,7 @@ impl Engine { } else { loop { match self.eval_stmt_block( - scope, global, caches, lib, this_ptr, body, true, level, + global, caches, lib, level, scope, this_ptr, body, true, ) { Ok(_) => (), Err(err) => match *err { @@ -511,7 +511,7 @@ impl Engine { loop { let condition = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, expr.position()) @@ -523,7 +523,7 @@ impl Engine { Ok(true) if body.is_empty() => (), Ok(true) => { match self.eval_stmt_block( - scope, global, caches, lib, this_ptr, body, true, level, + global, caches, lib, level, scope, this_ptr, body, true, ) { Ok(_) => (), Err(err) => match *err { @@ -546,7 +546,7 @@ impl Engine { loop { if !body.is_empty() { match self.eval_stmt_block( - scope, global, caches, lib, this_ptr, body, true, level, + global, caches, lib, level, scope, this_ptr, body, true, ) { Ok(_) => (), Err(err) => match *err { @@ -558,7 +558,7 @@ impl Engine { } let condition = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, expr.position()) @@ -578,7 +578,7 @@ impl Engine { let (var_name, counter, expr, statements) = &**x; let iter_result = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .map(Dynamic::flatten); if let Ok(iter_obj) = iter_result { @@ -652,7 +652,7 @@ impl Engine { } self.eval_stmt_block( - scope, global, caches, lib, this_ptr, statements, true, level, + global, caches, lib, level, scope, this_ptr, statements, true, ) .map(|_| Dynamic::UNIT) .or_else(|err| match *err { @@ -681,7 +681,7 @@ impl Engine { let is_break = options.contains(ASTFlags::BREAK); if let Some(ref expr) = expr { - self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| ERR::LoopBreak(is_break, v, *pos).into()) } else { Err(ERR::LoopBreak(is_break, Dynamic::UNIT, *pos).into()) @@ -700,7 +700,7 @@ impl Engine { } = &**x; let result = self - .eval_stmt_block(scope, global, caches, lib, this_ptr, try_block, true, level) + .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) .map(|_| Dynamic::UNIT); match result { @@ -750,14 +750,14 @@ impl Engine { } let result = self.eval_stmt_block( - scope, global, caches, lib, + level, + scope, this_ptr, catch_block, true, - level, ); scope.rewind(orig_scope_len); @@ -779,7 +779,7 @@ impl Engine { // Throw value Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), // Empty throw @@ -789,7 +789,7 @@ impl Engine { // Return value Stmt::Return(Some(expr), .., pos) => self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), // Empty return @@ -821,7 +821,7 @@ impl Engine { nesting_level, will_shadow, }; - let context = EvalContext::new(self, scope, global, None, lib, this_ptr, level); + let context = EvalContext::new(self, global, None, lib, level, scope, this_ptr); match filter(true, info, context) { Ok(true) => None, @@ -841,7 +841,7 @@ impl Engine { } else { // Evaluate initial value let value_result = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .map(Dynamic::flatten); if let Ok(mut value) = value_result { @@ -904,7 +904,7 @@ impl Engine { } let path_result = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .eval_expr(global, caches, lib, level, scope, this_ptr, expr) .and_then(|v| { let typ = v.type_name(); v.try_cast::().ok_or_else(|| { @@ -1016,15 +1016,15 @@ impl Engine { #[inline] pub(crate) fn eval_global_statements( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, - statements: &[Stmt], lib: &[&Module], level: usize, + scope: &mut Scope, + statements: &[Stmt], ) -> RhaiResult { self.eval_stmt_block( - scope, global, caches, lib, &mut None, statements, false, level, + global, caches, lib, level, scope, &mut None, statements, false, ) .or_else(|err| match *err { ERR::Return(out, ..) => Ok(out), diff --git a/src/func/call.rs b/src/func/call.rs index da03b7c9..6677ab32 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -326,13 +326,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, name: &str, op_token: Option<&Token>, hash: u64, args: &mut FnCallArgs, is_ref_mut: bool, pos: Position, - level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { self.track_operation(global, pos)?; @@ -416,7 +416,9 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self.run_debugger_raw(scope, global, lib, &mut None, node, event, level) { + match self + .run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) + { Ok(_) => (), Err(err) => _result = Err(err), } @@ -536,10 +538,11 @@ impl Engine { /// all others are silently replaced by `()`! pub(crate) fn exec_fn_call( &self, - _scope: Option<&mut Scope>, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + _scope: Option<&mut Scope>, fn_name: &str, op_token: Option<&Token>, hashes: FnCallHashes, @@ -547,7 +550,6 @@ impl Engine { is_ref_mut: bool, _is_method_call: bool, pos: Position, - level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> { Err(ERR::ErrorRuntime( @@ -654,16 +656,16 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - scope, global, caches, lib, + level, + scope, &mut Some(*first_arg), func, rest_args, true, pos, - level, ) } else { // Normal call of script function @@ -675,7 +677,7 @@ impl Engine { } let result = self.call_script_fn( - scope, global, caches, lib, &mut None, func, args, true, pos, level, + global, caches, lib, level, scope, &mut None, func, args, true, pos, ); // Restore the original reference @@ -695,7 +697,7 @@ impl Engine { let hash = hashes.native(); self.exec_native_fn_call( - global, caches, lib, fn_name, op_token, hash, args, is_ref_mut, pos, level, + global, caches, lib, level, fn_name, op_token, hash, args, is_ref_mut, pos, ) } @@ -703,20 +705,20 @@ impl Engine { #[inline] pub(crate) fn get_arg_value( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, arg_expr: &Expr, - level: usize, ) -> RhaiResultOf<(Dynamic, Position)> { // Literal values if let Some(value) = arg_expr.get_literal_value() { self.track_operation(global, arg_expr.start_position())?; #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, arg_expr, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, arg_expr)?; return Ok((value, arg_expr.start_position())); } @@ -727,7 +729,7 @@ impl Engine { matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) }); - let result = self.eval_expr(scope, global, caches, lib, this_ptr, arg_expr, level); + let result = self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr); // Restore function exit status #[cfg(feature = "debugging")] @@ -743,13 +745,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, mut call_args: &mut [Dynamic], first_arg_pos: Position, fn_call_pos: Position, - level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); @@ -781,10 +783,11 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - None, global, caches, lib, + level, + None, fn_name, None, new_hash, @@ -792,7 +795,6 @@ impl Engine { false, false, fn_call_pos, - level, ) } KEYWORD_FN_PTR_CALL => { @@ -837,10 +839,11 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - None, global, caches, lib, + level, + None, &fn_name, None, new_hash, @@ -848,7 +851,6 @@ impl Engine { is_ref_mut, true, fn_call_pos, - level, ) } KEYWORD_FN_PTR_CURRY => { @@ -938,10 +940,11 @@ impl Engine { args.extend(call_args.iter_mut()); self.exec_fn_call( - None, global, caches, lib, + level, + None, fn_name, None, hash, @@ -949,7 +952,6 @@ impl Engine { is_ref_mut, true, fn_call_pos, - level, ) } }?; @@ -965,10 +967,11 @@ impl Engine { /// Call a function in normal function-call style. pub(crate) fn make_function_call( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, op_token: Option<&Token>, @@ -977,7 +980,6 @@ impl Engine { hashes: FnCallHashes, capture_scope: bool, pos: Position, - level: usize, ) -> RhaiResult { let mut first_arg = first_arg; let mut a_expr = args_expr; @@ -994,7 +996,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1035,7 +1037,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; // Fn - only in function call style return arg_value @@ -1050,7 +1052,7 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, first, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1062,7 +1064,7 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { let (value, ..) = - self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1075,7 +1077,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = - self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; return Ok(arg_value.is_shared().into()); } @@ -1084,14 +1086,14 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, first, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg_value, arg_pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, &a_expr[0], level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, &a_expr[0])?; let num_params = arg_value .as_int() @@ -1110,7 +1112,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1125,12 +1127,12 @@ impl Engine { let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; + self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; let s = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; let result = - self.eval_script_expr_in_place(scope, global, caches, lib, s, pos, level + 1); + self.eval_script_expr_in_place(global, caches, lib, level + 1, scope, s, pos); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1172,7 +1174,7 @@ impl Engine { .copied() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) + self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1183,8 +1185,8 @@ impl Engine { return self .exec_fn_call( - scope, global, caches, lib, name, op_token, hashes, &mut args, is_ref_mut, - false, pos, level, + global, caches, lib, level, scope, name, op_token, hashes, &mut args, + is_ref_mut, false, pos, ) .map(|(v, ..)| v); } @@ -1200,16 +1202,16 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, first_expr, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, first_expr)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) + self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(scope, global, lib, this_ptr, first_expr, level)?; + self.search_namespace(global, caches, lib, level, scope, this_ptr, first_expr)?; if target.is_read_only() { target = target.into_owned(); @@ -1236,7 +1238,7 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) + self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1246,8 +1248,8 @@ impl Engine { } self.exec_fn_call( - None, global, caches, lib, name, op_token, hashes, &mut args, is_ref_mut, false, pos, - level, + global, caches, lib, level, None, name, op_token, hashes, &mut args, is_ref_mut, false, + pos, ) .map(|(v, ..)| v) } @@ -1256,17 +1258,17 @@ impl Engine { #[cfg(not(feature = "no_module"))] pub(crate) fn make_qualified_function_call( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, namespace: &crate::ast::Namespace, fn_name: &str, args_expr: &[Expr], hash: u64, pos: Position, - level: usize, ) -> RhaiResult { let mut arg_values = FnArgsVec::with_capacity(args_expr.len()); let mut args = FnArgsVec::with_capacity(args_expr.len()); @@ -1280,20 +1282,20 @@ impl Engine { // and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, &args_expr[0], level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, &args_expr[0])?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) + self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; // Get target reference to first argument let first_arg = &args_expr[0]; let (target, _pos) = - self.search_scope_only(scope, global, lib, this_ptr, first_arg, level)?; + self.search_scope_only(global, caches, lib, level, scope, this_ptr, first_arg)?; self.track_operation(global, _pos)?; @@ -1316,7 +1318,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) + self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1393,7 +1395,7 @@ impl Engine { 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, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, ); global.source = orig_source; @@ -1440,13 +1442,13 @@ impl Engine { /// Evaluate a text script in place - used primarily for 'eval'. pub(crate) fn eval_script_expr_in_place( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, script: &str, _pos: Position, - level: usize, ) -> RhaiResult { self.track_operation(global, _pos)?; @@ -1479,20 +1481,20 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(scope, global, caches, statements, lib, level) + self.eval_global_statements(global, caches, lib, level, scope, statements) } /// Evaluate a function call expression. pub(crate) fn eval_fn_call_expr( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, expr: &FnCallExpr, pos: Position, - level: usize, ) -> RhaiResult { let FnCallExpr { #[cfg(not(feature = "no_module"))] @@ -1510,12 +1512,12 @@ impl Engine { // Short-circuit native binary operator call if under Fast Operators mode if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self - .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? + .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[0])? .0 .flatten(); let mut rhs = self - .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? + .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[1])? .0 .flatten(); @@ -1531,8 +1533,8 @@ impl Engine { return self .exec_fn_call( - None, global, caches, lib, name, op_token, *hashes, operands, false, false, - pos, level, + global, caches, lib, level, None, name, op_token, *hashes, operands, false, + false, pos, ) .map(|(v, ..)| v); } @@ -1543,7 +1545,7 @@ impl Engine { let hash = hashes.native(); return self.make_qualified_function_call( - scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, + global, caches, lib, level, scope, this_ptr, namespace, name, args, hash, pos, ); } @@ -1554,8 +1556,8 @@ impl Engine { ); self.make_function_call( - scope, global, caches, lib, this_ptr, name, op_token, first_arg, args, *hashes, - *capture, pos, level, + global, caches, lib, level, scope, this_ptr, name, op_token, first_arg, args, *hashes, + *capture, pos, ) } } diff --git a/src/func/native.rs b/src/func/native.rs index 9d44125c..912f9d79 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -393,13 +393,13 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, + self.level + 1, fn_name, op_token, calc_fn_hash(None, fn_name, args_len), args, is_ref_mut, Position::NONE, - self.level + 1, ) .map(|(r, ..)| r); } @@ -418,10 +418,11 @@ impl<'a> NativeCallContext<'a> { self.engine() .exec_fn_call( - None, global, caches, self.lib, + self.level + 1, + None, fn_name, op_token, hash, @@ -429,7 +430,6 @@ impl<'a> NativeCallContext<'a> { is_ref_mut, is_method_call, Position::NONE, - self.level + 1, ) .map(|(r, ..)| r) } diff --git a/src/func/script.rs b/src/func/script.rs index 476343af..b80d74ae 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -24,16 +24,16 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn call_script_fn( &self, - scope: &mut Scope, global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], + level: usize, + scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, fn_def: &ScriptFnDef, args: &mut FnCallArgs, rewind_scope: bool, pos: Position, - level: usize, ) -> RhaiResult { #[cold] #[inline(never)] @@ -140,20 +140,20 @@ impl Engine { #[cfg(feature = "debugging")] { let node = crate::ast::Stmt::Noop(fn_def.body.position()); - self.run_debugger(scope, global, lib, this_ptr, &node, level)?; + self.run_debugger(global, caches, lib, level, scope, this_ptr, &node)?; } // Evaluate the function let mut _result = self .eval_stmt_block( - scope, global, caches, lib, + level, + scope, this_ptr, &fn_def.body, rewind_scope, - level, ) .or_else(|err| match *err { // Convert return statement to return value @@ -190,7 +190,9 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level) { + match self + .run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) + { Ok(_) => (), Err(err) => _result = Err(err), } diff --git a/src/module/mod.rs b/src/module/mod.rs index 59ead075..f0f1a982 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -3,6 +3,7 @@ #[cfg(feature = "metadata")] use crate::api::type_names::format_type; use crate::ast::FnAccess; +use crate::eval::Caches; use crate::func::{ shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, SendSync, @@ -1984,7 +1985,9 @@ impl Module { let orig_constants = std::mem::take(&mut global.constants); // Run the script - let result = engine.eval_ast_with_scope_raw(&mut scope, global, ast, 0); + let caches = &mut Caches::new(); + + let result = engine.eval_ast_with_scope_raw(global, caches, 0, &mut scope, ast); // Create new module let mut module = Module::new(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 6cb4beb2..bd7b0140 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -152,13 +152,13 @@ impl<'a> OptimizerState<'a> { &mut self.global, &mut self.caches, lib, + 0, fn_name, op_token, calc_fn_hash(None, fn_name, arg_values.len()), &mut arg_values.iter_mut().collect::>(), false, Position::NONE, - 0, ) .ok() .map(|(v, ..)| v) diff --git a/src/parser.rs b/src/parser.rs index 31391363..f0c3843f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2909,12 +2909,12 @@ impl Engine { let mut this_ptr = None; let context = EvalContext::new( self, - &mut state.stack, &mut state.global, None, &[], - &mut this_ptr, level, + &mut state.stack, + &mut this_ptr, ); match filter(false, info, context) { From 470af6af71f14724d2e8abfbff12d136a376808e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 21:59:49 +0800 Subject: [PATCH 099/116] Using hashing for full hash instead of xor. --- src/func/call.rs | 38 ++++++++++++++++++-------------------- src/func/hashing.rs | 20 ++++---------------- src/func/mod.rs | 4 +--- src/lib.rs | 2 +- src/module/mod.rs | 14 +++++++------- src/optimizer.rs | 7 +++---- 6 files changed, 34 insertions(+), 51 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index 6677ab32..817a8d7b 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -10,9 +10,8 @@ use crate::engine::{ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, - ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, - Scope, ERR, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -178,8 +177,7 @@ impl Engine { } let mut hash = args.as_ref().map_or(hash_base, |args| { - let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id())); - combine_hashes(hash_base, hash_params) + calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id())) }); let cache = caches.fn_resolution_cache_mut(); @@ -287,7 +285,8 @@ impl Engine { } // Try all permutations with `Dynamic` wildcards - let hash_params = calc_fn_params_hash( + hash = calc_fn_hash_full( + hash_base, args.as_ref() .expect("no permutations") .iter() @@ -302,7 +301,6 @@ impl Engine { } }), ); - hash = combine_hashes(hash_base, hash_params); bitmask += 1; } @@ -1335,9 +1333,7 @@ impl Engine { // Then search native Rust functions None => { self.track_operation(global, pos)?; - let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id())); - let hash_qualified_fn = combine_hashes(hash, hash_params); - + let hash_qualified_fn = calc_fn_hash_full(hash, args.iter().map(|a| a.type_id())); module.get_qualified_fn(hash_qualified_fn) } r => r, @@ -1355,16 +1351,18 @@ impl Engine { // Try all permutations with `Dynamic` wildcards while bitmask < max_bitmask { - let hash_params = calc_fn_params_hash(args.iter().enumerate().map(|(i, a)| { - let mask = 1usize << (num_args - i - 1); - if bitmask & mask == 0 { - a.type_id() - } else { - // Replace with `Dynamic` - TypeId::of::() - } - })); - let hash_qualified_fn = combine_hashes(hash, hash_params); + let hash_qualified_fn = calc_fn_hash_full( + hash, + args.iter().enumerate().map(|(i, a)| { + let mask = 1usize << (num_args - i - 1); + if bitmask & mask == 0 { + a.type_id() + } else { + // Replace with `Dynamic` + TypeId::of::() + } + }), + ); self.track_operation(global, pos)?; diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 855dcd3d..2b457c4d 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -156,7 +156,7 @@ pub fn calc_fn_hash<'a>( } } -/// Calculate a non-zero [`u64`] hash key from a list of parameter types. +/// Calculate a non-zero [`u64`] hash key from a base [`u64`] hash key and a list of parameter types. /// /// Parameter types are passed in via [`TypeId`] values from an iterator. /// @@ -165,10 +165,12 @@ pub fn calc_fn_hash<'a>( /// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline] #[must_use] -pub fn calc_fn_params_hash( +pub fn calc_fn_hash_full( + base: u64, params: impl IntoIterator>, ) -> u64 { let s = &mut get_hasher(); + base.hash(s); let iter = params.into_iter(); let len = iter.len(); iter.for_each(|t| { @@ -181,17 +183,3 @@ pub fn calc_fn_params_hash( r => r, } } - -/// Combine two [`u64`] hashes by taking the XOR of them. -/// -/// # Zeros -/// -/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. -#[inline(always)] -#[must_use] -pub const fn combine_hashes(a: u64, b: u64) -> u64 { - match a ^ b { - 0 => ALT_ZERO_HASH, - r => r, - } -} diff --git a/src/func/mod.rs b/src/func/mod.rs index 54652e74..6120764d 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -21,9 +21,7 @@ pub use call::FnCallArgs; pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; -pub use hashing::{ - calc_fn_hash, calc_fn_params_hash, calc_var_hash, combine_hashes, get_hasher, StraightHashMap, -}; +pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap}; pub use native::{ locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, FnAny, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared, diff --git a/src/lib.rs b/src/lib.rs index 8879012f..12f36850 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -238,7 +238,7 @@ pub use func::Shared; /// Alias to [`RefCell`][std::cell::RefCell] or [`RwLock`][std::sync::RwLock] depending on the `sync` feature flag. pub use func::Locked; -use func::{calc_fn_hash, calc_fn_params_hash, calc_var_hash, combine_hashes}; +use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash}; pub use rhai_codegen::*; diff --git a/src/module/mod.rs b/src/module/mod.rs index f0f1a982..68ef16e3 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,8 +10,8 @@ use crate::func::{ }; use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection}; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Identifier, ImmutableString, - NativeCallContext, RhaiResultOf, Shared, SmartString, StaticVec, + calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext, + RhaiResultOf, Shared, SmartString, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -151,9 +151,10 @@ pub fn calc_native_fn_hash<'a>( fn_name: &str, params: &[TypeId], ) -> u64 { - let hash_script = calc_fn_hash(modules, fn_name, params.len()); - let hash_params = calc_fn_params_hash(params.iter().copied()); - combine_hashes(hash_script, hash_params) + calc_fn_hash_full( + calc_fn_hash(modules, fn_name, params.len()), + params.iter().copied(), + ) } /// A module which may contain variables, sub-modules, external Rust functions, @@ -1028,8 +1029,7 @@ impl Module { let name = name.as_ref(); let hash_script = calc_fn_hash(None, name, param_types.len()); - let hash_params = calc_fn_params_hash(param_types.iter().copied()); - let hash_fn = combine_hashes(hash_script, hash_params); + let hash_fn = calc_fn_hash_full(hash_script, param_types.iter().copied()); if is_dynamic { self.dynamic_functions_filter.mark(hash_script); diff --git a/src/optimizer.rs b/src/optimizer.rs index bd7b0140..4428c6da 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -11,8 +11,8 @@ use crate::func::hashing::get_hasher; use crate::tokenizer::Token; use crate::types::dynamic::AccessMode; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier, - ImmutableString, Position, Scope, StaticVec, AST, INT, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position, + Scope, StaticVec, AST, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -171,8 +171,7 @@ fn has_native_fn_override( hash_script: u64, arg_types: impl AsRef<[TypeId]>, ) -> bool { - let hash_params = calc_fn_params_hash(arg_types.as_ref().iter().copied()); - let hash = combine_hashes(hash_script, hash_params); + let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied()); // First check the global namespace and packages, but skip modules that are standard because // they should never conflict with system functions. From 54aa89515e1535fc901f9f1fa1ee905adfc76f7d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 22:00:21 +0800 Subject: [PATCH 100/116] Fix no_module build. --- src/eval/expr.rs | 4 +++- src/module/mod.rs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 35da7117..0a9558da 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -68,7 +68,9 @@ impl Engine { self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) } #[cfg(feature = "no_module")] - (_, (), ..) => self.search_scope_only(scope, global, lib, this_ptr, expr), + (_, (), ..) => { + self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + } // Qualified variable access #[cfg(not(feature = "no_module"))] diff --git a/src/module/mod.rs b/src/module/mod.rs index 68ef16e3..6cb59198 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -3,7 +3,6 @@ #[cfg(feature = "metadata")] use crate::api::type_names::format_type; use crate::ast::FnAccess; -use crate::eval::Caches; use crate::func::{ shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, SendSync, @@ -1985,7 +1984,7 @@ impl Module { let orig_constants = std::mem::take(&mut global.constants); // Run the script - let caches = &mut Caches::new(); + let caches = &mut crate::eval::Caches::new(); let result = engine.eval_ast_with_scope_raw(global, caches, 0, &mut scope, ast); From 9463ff6211b3ef1c49dc0fd1568479e6833160ca Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 22:21:21 +0800 Subject: [PATCH 101/116] Add back nightly to CI. --- .github/workflows/build.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d186abb9..e1d91692 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,9 +71,8 @@ jobs: # smoketests for different toolchains - {toolchain: stable, os: windows-latest, experimental: false, flags: ""} - {toolchain: stable, os: macos-latest, experimental: false, flags: ""} - # data structure size changes - wait for beta to become stable and uncomment - #- {toolchain: beta, os: ubuntu-latest, experimental: false, flags: ""} - #- {toolchain: nightly, os: ubuntu-latest, experimental: true, flags: ""} + - {toolchain: beta, os: ubuntu-latest, experimental: false, flags: ""} + - {toolchain: nightly, os: ubuntu-latest, experimental: true, flags: ""} fail-fast: false steps: - name: Checkout From d20837ae5ede3b06944d8e5136c361e02b8e3693 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Nov 2022 22:49:52 +0800 Subject: [PATCH 102/116] Fix CI build. --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1d91692..996fc663 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -95,8 +95,6 @@ jobs: continue-on-error: ${{matrix.experimental}} strategy: matrix: - flags: - - "" include: - {os: ubuntu-latest, flags: "--profile unix", experimental: false} - {os: windows-latest, flags: "--profile windows", experimental: true} From 6e0f8b500771c8a8aa2d175316cee992b4e0746b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 5 Nov 2022 10:49:34 +0800 Subject: [PATCH 103/116] Fix no-std build. --- src/config/hashing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 58374d21..c11f7626 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -20,6 +20,7 @@ // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 use crate::config::hashing_env; +use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -27,7 +28,6 @@ use std::{ marker::PhantomData, mem, mem::MaybeUninit, - panic::{RefUnwindSafe, UnwindSafe}, sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; From 4455d95abc19151ec7414600efd8ea92abc58929 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 5 Nov 2022 11:35:01 +0800 Subject: [PATCH 104/116] Fix no-std by limiting ahash to 0.8.0. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3999b7de..4db19d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"] [dependencies] smallvec = { version = "1.7", default-features = false, features = ["union", "const_new", "const_generics"] } -ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +# 0.8.1 pulls in `getrandom/js` which breaks no-std +ahash = { version = "=0.8.0", default-features = false, features = ["compile-time-rng"] } num-traits = { version = "0.2", default-features = false } bitflags = { version = "1", default-features = false } smartstring = { version = "1", default-features = false } From c49948def4f7eeab9bc0a611f45556b8c88c6f4f Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Sat, 5 Nov 2022 22:46:08 +0900 Subject: [PATCH 105/116] address concerns 1, 2 --- src/config/hashing.rs | 19 +++++++++++-------- src/func/hashing.rs | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/config/hashing.rs b/src/config/hashing.rs index c11f7626..41bfa837 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -98,7 +98,7 @@ fn hokmalock(address: usize) -> &'static HokmaLock { #[must_use] struct SusLock where - T: 'static + Copy, + T: 'static, { initialized: AtomicBool, data: UnsafeCell>, @@ -107,7 +107,7 @@ where impl SusLock where - T: 'static + Copy, + T: 'static, { #[inline] pub const fn new() -> SusLock { @@ -119,14 +119,14 @@ where } #[must_use] - pub fn get(&self) -> Option { + pub fn get(&self) -> Option<&'static T> { if self.initialized.load(Ordering::SeqCst) { let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); // we forgo the optimistic read, because we don't really care let guard = hokma.write(); let val = { let cast: *const T = self.data.get().cast(); - unsafe { cast.read() } + unsafe { mem::transmute::<*const T, &'static T>(cast) } }; guard.the_price_of_silence(); Some(val) @@ -136,9 +136,9 @@ where } #[must_use] - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option { - let value = f(); + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&'static T> { if !self.initialized.load(Ordering::SeqCst) { + let value = f(); self.initialized.store(true, Ordering::SeqCst); let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); hokma.write(); @@ -216,6 +216,9 @@ pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]> /// See [`set_rhai_ahash_seed`] for more. #[inline] #[must_use] -pub fn get_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED).flatten() +pub fn get_ahash_seed() -> &Option<[u64; 4]> { + match AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED) { + Some(ash) => ash, + None => None, + } } diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 2b457c4d..83b2fed1 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -79,7 +79,7 @@ impl BuildHasher for StraightHasherBuilder { pub fn get_hasher() -> ahash::AHasher { match config::hashing::get_ahash_seed() { Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { - ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() + ahash::RandomState::with_seeds(*seed1, *seed2, *seed3, *seed4).build_hasher() } _ => ahash::AHasher::default(), } From a8ae82e3d8aa044d31f7080798bf9e91e80a2105 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Sat, 5 Nov 2022 22:53:58 +0900 Subject: [PATCH 106/116] add lifetime parameter --- src/config/hashing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 41bfa837..102586b0 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -216,7 +216,7 @@ pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]> /// See [`set_rhai_ahash_seed`] for more. #[inline] #[must_use] -pub fn get_ahash_seed() -> &Option<[u64; 4]> { +pub fn get_ahash_seed() -> &'static Option<[u64; 4]> { match AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED) { Some(ash) => ash, None => None, From 0c79471fd317c3bb6c2c08a0a245ad99e941ee15 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:18:59 +0800 Subject: [PATCH 107/116] Change lib to &[Shared] and remove dummy lifetimes. --- src/api/call_fn.rs | 6 +++--- src/api/eval.rs | 6 +++--- src/api/run.rs | 8 ++++---- src/eval/cache.rs | 8 ++------ src/eval/chaining.rs | 16 +++++++++------- src/eval/debugger.rs | 10 +++++----- src/eval/eval_context.rs | 24 ++++++++++++------------ src/eval/expr.rs | 12 ++++++------ src/eval/global_state.rs | 18 +++++++----------- src/eval/stmt.rs | 11 ++++++----- src/func/call.rs | 23 +++++++++++------------ src/func/mod.rs | 2 ++ src/func/script.rs | 10 +++++----- src/optimizer.rs | 20 ++++++++++---------- src/parser.rs | 2 +- src/types/fn_ptr.rs | 11 +++++++---- 16 files changed, 93 insertions(+), 94 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 1a9e2347..32510f60 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -4,8 +4,8 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::{ - reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, - ERR, + reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, + AST, ERR, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -248,7 +248,7 @@ impl Engine { arg_values: &mut [Dynamic], ) -> RhaiResult { let statements = ast.statements(); - let lib = &[ast.as_ref()]; + let lib = &[AsRef::>::as_ref(ast).clone()]; let mut this_ptr = this_ptr; let orig_scope_len = scope.len(); diff --git a/src/api/eval.rs b/src/api/eval.rs index ffef8623..02f21831 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -195,7 +195,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; @@ -234,7 +234,7 @@ impl Engine { let mut _lib = &[ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ][..]; #[cfg(not(feature = "no_function"))] if !ast.has_functions() { @@ -264,7 +264,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&crate::Module], + lib: &[crate::Shared], level: usize, scope: &mut Scope, statements: &[crate::ast::Stmt], diff --git a/src/api/run.rs b/src/api/run.rs index effdd4b6..96faee2e 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,7 +2,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; -use crate::{Engine, Module, RhaiResultOf, Scope, AST}; +use crate::{Engine, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -124,9 +124,9 @@ impl Engine { if !statements.is_empty() { let lib = [ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; - let lib = if lib.first().map_or(true, |m: &&Module| m.is_empty()) { + let lib = if lib.first().map_or(true, |m| m.is_empty()) { &lib[0..0] } else { &lib @@ -139,7 +139,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; diff --git a/src/eval/cache.rs b/src/eval/cache.rs index b71eb8cf..ee4f07a3 100644 --- a/src/eval/cache.rs +++ b/src/eval/cache.rs @@ -3,7 +3,6 @@ use crate::func::{CallableFunction, StraightHashMap}; use crate::types::BloomFilterU64; use crate::{ImmutableString, StaticVec}; -use std::marker::PhantomData; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -45,21 +44,18 @@ impl FnResolutionCache { /// The following caches are contained inside this type: /// * A stack of [function resolution caches][FnResolutionCache] #[derive(Debug, Clone)] -pub struct Caches<'a> { +pub struct Caches { /// Stack of [function resolution caches][FnResolutionCache]. stack: StaticVec, - /// Take care of the lifetime parameter. - dummy: PhantomData<&'a ()>, } -impl Caches<'_> { +impl Caches { /// Create an empty [`Caches`]. #[inline(always)] #[must_use] pub const fn new() -> Self { Self { stack: StaticVec::new_const(), - dummy: PhantomData, } } /// Get the number of function resolution cache(s) in the stack. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index ecc40fc7..cf51a465 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -4,7 +4,9 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; -use crate::{Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; +use crate::{ + Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, +}; use std::hash::Hash; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -40,7 +42,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, @@ -555,7 +557,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -646,7 +648,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -758,7 +760,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -781,7 +783,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -805,7 +807,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 9b516fdf..3048e5bc 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{ - Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, + Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, Shared, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -413,7 +413,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -440,7 +440,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -463,7 +463,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -510,7 +510,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index 172720cd..99456ce1 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -1,39 +1,39 @@ //! Evaluation context. use super::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Scope}; +use crate::{Dynamic, Engine, Module, Scope, Shared}; #[cfg(feature = "no_std")] use std::prelude::v1::*; /// Context of a script evaluation process. #[derive(Debug)] #[allow(dead_code)] -pub struct EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> { +pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current [`Engine`]. engine: &'a Engine, /// The current [`Scope`]. scope: &'s mut Scope<'ps>, /// The current [`GlobalRuntimeState`]. - global: &'g mut GlobalRuntimeState<'pg>, + global: &'g mut GlobalRuntimeState, /// The current [caches][Caches], if available. - caches: Option<&'c mut Caches<'pc>>, + caches: Option<&'c mut Caches>, /// The current stack of imported [modules][Module]. - lib: &'a [&'a Module], + lib: &'a [Shared], /// The current bound `this` pointer, if any. this_ptr: &'t mut Option<&'pt mut Dynamic>, /// The current nesting level of function calls. level: usize, } -impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> { +impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// Create a new [`EvalContext`]. #[inline(always)] #[must_use] pub fn new( engine: &'a Engine, - global: &'g mut GlobalRuntimeState<'pg>, - caches: Option<&'c mut Caches<'pc>>, - lib: &'a [&'a Module], + global: &'g mut GlobalRuntimeState, + caches: Option<&'c mut Caches>, + lib: &'a [Shared], level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Option<&'pt mut Dynamic>, @@ -104,20 +104,20 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, ' #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState<'pg> { + pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState { &mut self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().copied() + self.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[&Module] { + pub const fn namespaces(&self) -> &[Shared] { self.lib } /// The current bound `this` pointer, if any. diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 0a9558da..afa47ce4 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; -use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; +use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -18,7 +18,7 @@ impl Engine { &self, global: &GlobalRuntimeState, namespace: &crate::ast::Namespace, - ) -> Option> { + ) -> Option> { assert!(!namespace.is_empty()); let root = namespace.root(); @@ -51,7 +51,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -137,7 +137,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -160,7 +160,7 @@ impl Engine { Expr::Variable(v, None, pos) if lib .iter() - .flat_map(|&m| m.iter_script_fn()) + .flat_map(|m| m.iter_script_fn()) .any(|(_, _, f, ..)| f == v.3.as_str()) => { let val: Dynamic = @@ -221,7 +221,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index db4c5332..935434ef 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -1,9 +1,9 @@ //! Global runtime state. use crate::{Dynamic, Engine, ImmutableString}; +use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{fmt, marker::PhantomData}; /// Collection of globally-defined constants. #[cfg(not(feature = "no_module"))] @@ -22,7 +22,7 @@ pub type GlobalConstants = // Most usage will be looking up a particular key from the list and then getting the module that // corresponds to that key. #[derive(Clone)] -pub struct GlobalRuntimeState<'a> { +pub struct GlobalRuntimeState { /// Names of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] imports: crate::StaticVec, @@ -70,11 +70,9 @@ pub struct GlobalRuntimeState<'a> { /// Debugging interface. #[cfg(feature = "debugging")] pub debugger: super::Debugger, - /// Take care of the lifetime parameter. - dummy: PhantomData<&'a ()>, } -impl GlobalRuntimeState<'_> { +impl GlobalRuntimeState { /// Create a new [`GlobalRuntimeState`] based on an [`Engine`]. #[inline(always)] #[must_use] @@ -112,8 +110,6 @@ impl GlobalRuntimeState<'_> { None => Dynamic::UNIT, }, ), - - dummy: PhantomData::default(), } } /// Get the length of the stack of globally-imported [modules][crate::Module]. @@ -319,7 +315,7 @@ impl GlobalRuntimeState<'_> { } #[cfg(not(feature = "no_module"))] -impl IntoIterator for GlobalRuntimeState<'_> { +impl IntoIterator for GlobalRuntimeState { type Item = (ImmutableString, crate::Shared); type IntoIter = std::iter::Rev< std::iter::Zip< @@ -334,7 +330,7 @@ impl IntoIterator for GlobalRuntimeState<'_> { } #[cfg(not(feature = "no_module"))] -impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { +impl<'a> IntoIterator for &'a GlobalRuntimeState { type Item = (&'a ImmutableString, &'a crate::Shared); type IntoIter = std::iter::Rev< std::iter::Zip< @@ -350,7 +346,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] impl, M: Into>> Extend<(K, M)> - for GlobalRuntimeState<'_> + for GlobalRuntimeState { #[inline] fn extend>(&mut self, iter: T) { @@ -361,7 +357,7 @@ impl, M: Into>> Extend<(K, } } -impl fmt::Debug for GlobalRuntimeState<'_> { +impl fmt::Debug for GlobalRuntimeState { #[cold] #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 5bf03093..9ccde0a0 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -8,7 +8,8 @@ use crate::ast::{ use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; use crate::{ - Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, + Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, + ERR, INT, }; use std::hash::{Hash, Hasher}; #[cfg(feature = "no_std")] @@ -27,7 +28,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -112,7 +113,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, op_info: &OpAssignment, target: &mut Target, @@ -196,7 +197,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1018,7 +1019,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, statements: &[Stmt], diff --git a/src/func/call.rs b/src/func/call.rs index 817a8d7b..ff95e3a4 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,7 +11,7 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -166,7 +166,7 @@ impl Engine { _global: &GlobalRuntimeState, caches: &'s mut Caches, local_entry: &'s mut Option, - lib: &[&Module], + lib: &[Shared], op_token: Option<&Token>, hash_base: u64, args: Option<&mut FnCallArgs>, @@ -193,8 +193,7 @@ impl Engine { loop { let func = lib .iter() - .copied() - .chain(self.global_modules.iter().map(|m| m.as_ref())) + .chain(self.global_modules.iter()) .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw()))); #[cfg(not(feature = "no_module"))] @@ -323,7 +322,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, name: &str, op_token: Option<&Token>, @@ -538,7 +537,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, _scope: Option<&mut Scope>, fn_name: &str, @@ -705,7 +704,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -742,7 +741,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, fn_name: &str, mut hash: FnCallHashes, @@ -967,7 +966,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1258,7 +1257,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1442,7 +1441,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, script: &str, @@ -1487,7 +1486,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/func/mod.rs b/src/func/mod.rs index 6120764d..d5659854 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -22,6 +22,8 @@ pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap}; +#[cfg(feature = "internals")] +pub use native::NativeCallContextStore; pub use native::{ locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, FnAny, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared, diff --git a/src/func/script.rs b/src/func/script.rs index b80d74ae..06fa916a 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -4,7 +4,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::eval::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, ERR}; +use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, Shared, ERR}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +26,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -127,8 +127,8 @@ impl Engine { lib } else { caches.push_fn_resolution_cache(); - lib_merged.push(&**fn_lib); - lib_merged.extend(lib.iter().copied()); + lib_merged.push(fn_lib.clone()); + lib_merged.extend(lib.iter().cloned()); &lib_merged }, Some(mem::replace(&mut global.constants, constants.clone())), @@ -230,7 +230,7 @@ impl Engine { &self, _global: Option<&GlobalRuntimeState>, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], hash_script: u64, ) -> bool { let cache = caches.fn_resolution_cache_mut(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 4428c6da..aa204777 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -56,12 +56,12 @@ struct OptimizerState<'a> { /// An [`Engine`] instance for eager function evaluation. engine: &'a Engine, /// The global runtime state. - global: GlobalRuntimeState<'a>, + global: GlobalRuntimeState, /// Function resolution caches. - caches: Caches<'a>, + caches: Caches, /// [Module][crate::Module] containing script-defined functions. #[cfg(not(feature = "no_function"))] - lib: &'a [&'a crate::Module], + lib: &'a [crate::Shared], /// Optimization level. optimization_level: OptimizationLevel, } @@ -71,7 +71,7 @@ impl<'a> OptimizerState<'a> { #[inline(always)] pub fn new( engine: &'a Engine, - #[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module], + #[cfg(not(feature = "no_function"))] lib: &'a [crate::Shared], optimization_level: OptimizationLevel, ) -> Self { Self { @@ -1189,7 +1189,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some(); + let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some(); #[cfg(feature = "no_function")] let has_script_fn = false; @@ -1263,7 +1263,7 @@ fn optimize_top_level( statements: StmtBlockContainer, engine: &Engine, scope: &Scope, - #[cfg(not(feature = "no_function"))] lib: &[&crate::Module], + #[cfg(not(feature = "no_function"))] lib: &[crate::Shared], optimization_level: OptimizationLevel, ) -> StmtBlockContainer { let mut statements = statements; @@ -1317,7 +1317,7 @@ pub fn optimize_into_ast( let mut statements = statements; #[cfg(not(feature = "no_function"))] - let lib = { + let lib: crate::Shared<_> = { let mut module = crate::Module::new(); if optimization_level != OptimizationLevel::None { @@ -1338,7 +1338,7 @@ pub fn optimize_into_ast( }); } - let lib2 = &[&lib2]; + let lib2 = &[lib2.into()]; for fn_def in functions { let mut fn_def = crate::func::shared_take_or_clone(fn_def); @@ -1356,7 +1356,7 @@ pub fn optimize_into_ast( } } - module + module.into() }; statements.shrink_to_fit(); @@ -1369,7 +1369,7 @@ pub fn optimize_into_ast( engine, scope, #[cfg(not(feature = "no_function"))] - &[&lib], + &[lib.clone()], optimization_level, ), }, diff --git a/src/parser.rs b/src/parser.rs index f0c3843f..ee107089 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -55,7 +55,7 @@ pub struct ParseState<'e> { /// External [scope][Scope] with constants. pub scope: &'e Scope<'e>, /// Global runtime state. - pub global: GlobalRuntimeState<'e>, + pub global: GlobalRuntimeState, /// Encapsulates a local stack with variable names to simulate an actual runtime scope. pub stack: Scope<'e>, /// Size of the local variables stack upon entry of the current block scope. diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 5938b7a6..810dd5a8 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -3,8 +3,8 @@ use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError, - RhaiResult, RhaiResultOf, StaticVec, AST, ERR, + Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult, + RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -152,13 +152,16 @@ impl FnPtr { let lib = [ #[cfg(not(feature = "no_function"))] - _ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; - let lib = if lib.first().map_or(true, |m: &&Module| m.is_empty()) { + let lib = if lib.first().map_or(true, |m| m.is_empty()) { &lib[0..0] } else { &lib }; + #[cfg(feature = "no_function")] + let lib = &[]; + #[allow(deprecated)] let ctx = NativeCallContext::new(engine, self.fn_name(), lib); From b4529b6a6400c7cb0976e4e8a457f0bf5511616f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:19:10 +0800 Subject: [PATCH 108/116] Add storage API for NativeCallContext. --- CHANGELOG.md | 5 +++ src/func/native.rs | 84 ++++++++++++++++++++++++++++++++++++++++------ tests/modules.rs | 41 +++------------------- 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f73ee408..3a606c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,11 @@ New features * `Scope` is now serializable and deserializable via `serde`. +### Store and recreate `NativeCallContext` + +* A convenient API is added to store a `NativeCallContext` into a new `NativeCallContextStore` type. +* This allows a `NativeCallContext` to be stored and recreated later on. + ### Call native Rust functions in `NativeCallContext` * `NativeCallContext::call_native_fn` is added to call registered native Rust functions only. diff --git a/src/func/native.rs b/src/func/native.rs index 912f9d79..63823ecd 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -72,22 +72,51 @@ pub struct NativeCallContext<'a> { /// Function source, if any. source: Option<&'a str>, /// The current [`GlobalRuntimeState`], if any. - global: Option<&'a GlobalRuntimeState<'a>>, + global: Option<&'a GlobalRuntimeState>, /// The current stack of loaded [modules][Module]. - lib: &'a [&'a Module], + lib: &'a [Shared], /// [Position] of the function call. pos: Position, /// The current nesting level of function calls. level: usize, } +/// _(internals)_ Context of a native Rust function call. +/// Exported under the `internals` feature only. +#[cfg(feature = "internals")] +#[derive(Debug, Clone)] +pub struct NativeCallContextStore { + /// Name of function called. + pub fn_name: String, + /// Function source, if any. + pub source: Option, + /// The current [`GlobalRuntimeState`], if any. + pub global: GlobalRuntimeState, + /// The current stack of loaded [modules][Module]. + pub lib: StaticVec>, + /// [Position] of the function call. + pub pos: Position, + /// The current nesting level of function calls. + pub level: usize, +} + +#[cfg(feature = "internals")] +impl NativeCallContextStore { + /// Create a [`NativeCallContext`] from a [`NativeCallContextClone`]. + #[inline(always)] + #[must_use] + pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> { + NativeCallContext::from_stored_data(engine, self) + } +} + impl<'a> From<( &'a Engine, &'a str, Option<&'a str>, - &'a GlobalRuntimeState<'a>, - &'a [&Module], + &'a GlobalRuntimeState, + &'a [Shared], Position, usize, )> for NativeCallContext<'a> @@ -99,7 +128,7 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [&Module], + &'a [Shared], Position, usize, ), @@ -116,9 +145,9 @@ impl<'a> } } -impl<'a> From<(&'a Engine, &'a str, &'a [&'a Module])> for NativeCallContext<'a> { +impl<'a> From<(&'a Engine, &'a str, &'a [Shared])> for NativeCallContext<'a> { #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [&Module])) -> Self { + fn from(value: (&'a Engine, &'a str, &'a [Shared])) -> Self { Self { engine: value.0, fn_name: value.1, @@ -140,7 +169,7 @@ impl<'a> NativeCallContext<'a> { )] #[inline(always)] #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { + pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [Shared]) -> Self { Self { engine, fn_name, @@ -164,7 +193,7 @@ impl<'a> NativeCallContext<'a> { fn_name: &'a str, source: Option<&'a str>, global: &'a GlobalRuntimeState, - lib: &'a [&Module], + lib: &'a [Shared], pos: Position, level: usize, ) -> Self { @@ -178,6 +207,39 @@ impl<'a> NativeCallContext<'a> { level, } } + + /// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`]. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline] + #[must_use] + pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self { + Self { + engine, + fn_name: &context.fn_name, + source: context.source.as_ref().map(String::as_str), + global: Some(&context.global), + lib: &context.lib, + pos: context.pos, + level: context.level, + } + } + /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`]. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline] + #[must_use] + pub fn store_data(&self) -> NativeCallContextStore { + NativeCallContextStore { + fn_name: self.fn_name.to_string(), + source: self.source.map(|s| s.to_string()), + global: self.global.unwrap().clone(), + lib: self.lib.iter().cloned().collect(), + pos: self.pos, + level: self.level, + } + } + /// The current [`Engine`]. #[inline(always)] #[must_use] @@ -246,14 +308,14 @@ impl<'a> NativeCallContext<'a> { /// in reverse order (i.e. parent namespaces are iterated after child namespaces). #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().copied() + self.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[&Module] { + pub const fn namespaces(&self) -> &[Shared] { self.lib } /// Call a function inside the call context with the provided arguments. diff --git a/tests/modules.rs b/tests/modules.rs index f18d9aa7..babc862a 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -2,7 +2,7 @@ use rhai::{ module_resolvers::{DummyModuleResolver, StaticModuleResolver}, Dynamic, Engine, EvalAltResult, FnNamespace, FnPtr, ImmutableString, Module, NativeCallContext, - ParseError, ParseErrorType, Scope, Shared, INT, + ParseError, ParseErrorType, Scope, INT, }; #[test] @@ -546,44 +546,13 @@ fn test_module_context() -> Result<(), Box> { engine.register_fn( "calc", |context: NativeCallContext, fp: FnPtr| -> Result> { - // Store fields for later use let engine = context.engine(); - let fn_name = context.fn_name().to_string(); - let source = context.source().map(|s| s.to_string()); - let global = context.global_runtime_state().unwrap().clone(); - let pos = context.position(); - let call_level = context.call_level(); - // Store the paths of the stack of call modules up to this point - let modules_list: Vec = context - .iter_namespaces() - .map(|m| m.id().unwrap_or("testing")) - .filter(|id| !id.is_empty()) - .map(|id| id.to_string()) - .collect(); + // Store context for later use - requires the 'internals' feature + let context_data = context.store_data(); - // Recreate the 'NativeCallContext' - requires the 'internals' feature - let mut libraries = Vec::>::new(); - - for path in modules_list { - // Recreate the stack of call modules by resolving each path with - // the module resolver. - let module = engine.module_resolver().resolve(engine, None, &path, pos)?; - - libraries.push(module); - } - - let lib: Vec<&Module> = libraries.iter().map(|m| m.as_ref()).collect(); - - let new_context = NativeCallContext::new_with_all_fields( - engine, - &fn_name, - source.as_ref().map(String::as_str), - &global, - &lib, - pos, - call_level, - ); + // Recreate the 'NativeCallContext' + let new_context = context_data.create_context(engine); fp.call_within_context(&new_context, (41 as INT,)) }, From cba914db95bb6b5a6c176dab9a152a0186e5215a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:34:25 +0800 Subject: [PATCH 109/116] Fix no_function build. --- src/api/run.rs | 6 +++--- src/types/fn_ptr.rs | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/api/run.rs b/src/api/run.rs index 96faee2e..34f1d106 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -122,12 +122,12 @@ impl Engine { let statements = ast.statements(); if !statements.is_empty() { - let lib = [ + let lib: &[crate::Shared] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::>::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { - &lib[0..0] + &[][..] } else { &lib }; diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 810dd5a8..b7ac5468 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -150,17 +150,15 @@ impl FnPtr { let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); - let lib = [ + let lib: &[crate::Shared] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::>::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { - &lib[0..0] + &[][..] } else { &lib }; - #[cfg(feature = "no_function")] - let lib = &[]; #[allow(deprecated)] let ctx = NativeCallContext::new(engine, self.fn_name(), lib); From f4e290135368eecf6c1da08e41c4a47665ad34a8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 11:52:46 +0800 Subject: [PATCH 110/116] Use RestoreOnDrop. --- src/api/call_fn.rs | 14 +- src/api/eval.rs | 22 +- src/eval/chaining.rs | 93 ++--- src/eval/expr.rs | 99 ++--- src/eval/stmt.rs | 816 ++++++++++++++++++------------------------ src/func/call.rs | 145 ++++---- src/types/interner.rs | 69 ++-- src/types/mod.rs | 2 + src/types/restore.rs | 57 +++ 9 files changed, 618 insertions(+), 699 deletions(-) create mode 100644 src/types/restore.rs diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 32510f60..2057d9b7 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -3,6 +3,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; +use crate::types::RestoreOnDrop; use crate::{ reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, AST, ERR, @@ -258,6 +259,10 @@ impl Engine { &mut global.embedded_module_resolver, ast.resolver().cloned(), ); + #[cfg(not(feature = "no_module"))] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.embedded_module_resolver = orig_embedded_module_resolver + }); let result = if eval_ast && !statements.is_empty() { let r = self.eval_global_statements(global, caches, lib, 0, scope, statements); @@ -293,14 +298,7 @@ impl Engine { } else { Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()) } - }); - - #[cfg(not(feature = "no_module"))] - { - global.embedded_module_resolver = orig_embedded_module_resolver; - } - - let result = result?; + })?; #[cfg(feature = "debugging")] if self.debugger.is_some() { diff --git a/src/api/eval.rs b/src/api/eval.rs index 02f21831..294bf89a 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,6 +3,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; use crate::types::dynamic::Variant; +use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR, }; @@ -225,6 +226,10 @@ impl Engine { &mut global.embedded_module_resolver, ast.resolver().cloned(), ); + #[cfg(not(feature = "no_module"))] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.embedded_module_resolver = orig_embedded_module_resolver + }); let statements = ast.statements(); @@ -232,23 +237,12 @@ impl Engine { return Ok(Dynamic::UNIT); } - let mut _lib = &[ + let lib = &[ #[cfg(not(feature = "no_function"))] AsRef::>::as_ref(ast).clone(), - ][..]; - #[cfg(not(feature = "no_function"))] - if !ast.has_functions() { - _lib = &[]; - } + ]; - let result = self.eval_global_statements(global, caches, _lib, level, scope, statements); - - #[cfg(not(feature = "no_module"))] - { - global.embedded_module_resolver = orig_embedded_module_resolver; - } - - result + self.eval_global_statements(global, caches, lib, level, scope, statements) } /// _(internals)_ Evaluate a list of statements with no `this` pointer. /// Exported under the `internals` feature only. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index cf51a465..7d7560fc 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -4,6 +4,7 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; +use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, }; @@ -200,29 +201,30 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset_debugger = self.run_debugger_with_reset( + let reset = self.run_debugger_with_reset( global, caches, lib, level, scope, this_ptr, rhs, )?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.debugger.reset_status(reset) + }); let crate::ast::FnCallExpr { name, hashes, args, .. } = &**x; + // Truncate the index values upon exit let offset = idx_values.len() - args.len(); + let idx_values = + &mut *RestoreOnDrop::new(idx_values, move |v| v.truncate(offset)); + let call_args = &mut idx_values[offset..]; let pos1 = args.get(0).map_or(Position::NONE, Expr::position); - let result = self.make_method_call( + self.make_method_call( global, caches, lib, level, name, *hashes, target, call_args, pos1, *pos, - ); - - idx_values.truncate(offset); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result + ) } // xxx.fn_name(...) = ??? Expr::MethodCall(..) if new_val.is_some() => { @@ -378,29 +380,33 @@ impl Engine { // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] - let reset_debugger = self.run_debugger_with_reset( + let reset = self.run_debugger_with_reset( global, caches, lib, level, scope, this_ptr, _node, )?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.debugger.reset_status(reset) + }); let crate::ast::FnCallExpr { name, hashes, args, .. } = &**x; + // Truncate the index values upon exit let offset = idx_values.len() - args.len(); + let idx_values = &mut *RestoreOnDrop::new(idx_values, move |v| { + v.truncate(offset) + }); + let call_args = &mut idx_values[offset..]; let pos1 = args.get(0).map_or(Position::NONE, Expr::position); - let result = self.make_method_call( + self.make_method_call( global, caches, lib, level, name, *hashes, target, call_args, pos1, pos, - ); - - idx_values.truncate(offset); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result?.0.into() + )? + .0 + .into() } // {xxx:map}.module::fn_name(...) - syntax error Expr::MethodCall(..) => unreachable!( @@ -504,32 +510,39 @@ impl Engine { } // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr Expr::MethodCall(ref f, pos) if !f.is_qualified() => { - #[cfg(feature = "debugging")] - let reset_debugger = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, - )?; + let val = { + #[cfg(feature = "debugging")] + let reset = self.run_debugger_with_reset( + global, caches, lib, level, scope, this_ptr, _node, + )?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.debugger.reset_status(reset) + }); - let crate::ast::FnCallExpr { - name, hashes, args, .. - } = &**f; - let rhs_chain = rhs.into(); + let crate::ast::FnCallExpr { + name, hashes, args, .. + } = &**f; - let offset = idx_values.len() - args.len(); - let call_args = &mut idx_values[offset..]; - let pos1 = args.get(0).map_or(Position::NONE, Expr::position); + // Truncate the index values upon exit + let offset = idx_values.len() - args.len(); + let idx_values = + &mut *RestoreOnDrop::new(idx_values, move |v| { + v.truncate(offset) + }); - let result = self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, - pos1, pos, - ); + let call_args = &mut idx_values[offset..]; + let pos1 = args.get(0).map_or(Position::NONE, Expr::position); - idx_values.truncate(offset); + self.make_method_call( + global, caches, lib, level, name, *hashes, target, + call_args, pos1, pos, + )? + .0 + }; - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - let (val, _) = &mut result?; let val = &mut val.into(); + let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( global, caches, lib, level, this_ptr, val, root, rhs, *options, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index afa47ce4..269ab992 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -4,6 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; +use crate::types::RestoreOnDrop; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] @@ -234,18 +235,14 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset_debugger = + let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); self.track_operation(global, expr.position())?; - let result = - self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - return result; + return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); } // Then variable access. @@ -269,12 +266,14 @@ impl Engine { } #[cfg(feature = "debugging")] - let reset_debugger = + let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); self.track_operation(global, expr.position())?; - let result = match expr { + match expr { // Constants Expr::DynamicConstant(x, ..) => Ok(x.as_ref().clone()), Expr::IntegerConstant(x, ..) => Ok((*x).into()), @@ -374,60 +373,33 @@ impl Engine { .map(Into::into) } - Expr::And(x, ..) => { - let lhs = self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, x.lhs.position()) - }) - }); + Expr::And(x, ..) => Ok((self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? + && self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) + .into()), - match lhs { - Ok(true) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) - .and_then(|v| { - v.as_bool() - .map_err(|typ| { - self.make_type_mismatch_err::(typ, x.rhs.position()) - }) - .map(Into::into) - }), - _ => lhs.map(Into::into), - } - } - - Expr::Or(x, ..) => { - let lhs = self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, x.lhs.position()) - }) - }); - - match lhs { - Ok(false) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) - .and_then(|v| { - v.as_bool() - .map_err(|typ| { - self.make_type_mismatch_err::(typ, x.rhs.position()) - }) - .map(Into::into) - }), - _ => lhs.map(Into::into), - } - } + Expr::Or(x, ..) => Ok((self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? + || self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) + .into()), Expr::Coalesce(x, ..) => { - let lhs = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs); + let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)?; - match lhs { - Ok(value) if value.is::<()>() => { - self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) - } - _ => lhs, + if value.is::<()>() { + self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) + } else { + Ok(value) } } @@ -467,11 +439,6 @@ impl Engine { .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), _ => unreachable!("expression cannot be evaluated: {:?}", expr), - }; - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result + } } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 9ccde0a0..2841ecba 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -7,6 +7,7 @@ use crate::ast::{ }; use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; +use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, INT, @@ -39,17 +40,39 @@ impl Engine { return Ok(Dynamic::UNIT); } - let orig_always_search_scope = global.always_search_scope; + // Restore scope at end of block if necessary let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::new_if(restore_orig_state, scope, move |s| { + s.rewind(orig_scope_len); + }); + + // Restore global state at end of block if necessary + let orig_always_search_scope = global.always_search_scope; #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); - let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); if restore_orig_state { global.scope_level += 1; } - let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { + let global = &mut *RestoreOnDrop::new_if(restore_orig_state, global, move |g| { + g.scope_level -= 1; + #[cfg(not(feature = "no_module"))] + g.truncate_imports(orig_imports_len); + + // The impact of new local variables goes away at the end of a block + // because any new variables introduced will go out of scope + g.always_search_scope = orig_always_search_scope; + }); + + // Pop new function resolution caches at end of block + let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); + let caches = &mut *RestoreOnDrop::new(caches, move |c| { + c.rewind_fn_resolution_caches(orig_fn_resolution_caches_len) + }); + + // Run the statements + statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { #[cfg(not(feature = "no_module"))] let imports_len = global.num_imports(); @@ -89,23 +112,7 @@ impl Engine { } Ok(result) - }); - - // If imports list is modified, pop the functions lookup cache - caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); - - if restore_orig_state { - scope.rewind(orig_scope_len); - global.scope_level -= 1; - #[cfg(not(feature = "no_module"))] - global.truncate_imports(orig_imports_len); - - // The impact of new local variables goes away at the end of a block - // because any new variables introduced will go out of scope - global.always_search_scope = orig_always_search_scope; - } - - result + }) } /// Evaluate an op-assignment statement. @@ -205,8 +212,10 @@ impl Engine { rewind_scope: bool, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset_debugger = + let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -215,13 +224,7 @@ impl Engine { if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; - let result = - self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - return result; + return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); } // Then assignments. @@ -232,108 +235,85 @@ impl Engine { self.track_operation(global, stmt.position())?; - let result = if let Expr::Variable(x, ..) = lhs { - let rhs_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, rhs) - .map(Dynamic::flatten); + if let Expr::Variable(x, ..) = lhs { + let rhs_val = self + .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? + .flatten(); - if let Ok(rhs_val) = rhs_result { - let search_result = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs); + let search_val = + self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; - if let Ok(search_val) = search_result { - let (mut lhs_ptr, pos) = search_val; + let (mut lhs_ptr, pos) = search_val; - let var_name = x.3.as_str(); + let var_name = x.3.as_str(); - #[cfg(not(feature = "no_closure"))] - // Also handle case where target is a `Dynamic` shared value - // (returned by a variable resolver, for example) - let is_temp_result = !lhs_ptr.is_ref() && !lhs_ptr.is_shared(); - #[cfg(feature = "no_closure")] - let is_temp_result = !lhs_ptr.is_ref(); + #[cfg(not(feature = "no_closure"))] + // Also handle case where target is a `Dynamic` shared value + // (returned by a variable resolver, for example) + let is_temp_result = !lhs_ptr.is_ref() && !lhs_ptr.is_shared(); + #[cfg(feature = "no_closure")] + let is_temp_result = !lhs_ptr.is_ref(); - // Cannot assign to temp result from expression - if is_temp_result { - return Err( - ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into() - ); - } - - self.track_operation(global, pos)?; - - let root = (var_name, pos); - let lhs_ptr = &mut lhs_ptr; - - self.eval_op_assignment( - global, caches, lib, level, op_info, lhs_ptr, root, rhs_val, - ) - .map(|_| Dynamic::UNIT) - } else { - search_result.map(|_| Dynamic::UNIT) - } - } else { - rhs_result + // Cannot assign to temp result from expression + if is_temp_result { + return Err(ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into()); } + + self.track_operation(global, pos)?; + + let root = (var_name, pos); + let lhs_ptr = &mut lhs_ptr; + + return self + .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) + .map(|_| Dynamic::UNIT); } else { let (op_info, BinaryExpr { lhs, rhs }) = &**x; - let rhs_result = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs); + let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; - if let Ok(rhs_val) = rhs_result { - // Check if the result is a string. If so, intern it. - #[cfg(not(feature = "no_closure"))] - let is_string = !rhs_val.is_shared() && rhs_val.is::(); - #[cfg(feature = "no_closure")] - let is_string = rhs_val.is::(); + // Check if the result is a string. If so, intern it. + #[cfg(not(feature = "no_closure"))] + let is_string = !rhs_val.is_shared() && rhs_val.is::(); + #[cfg(feature = "no_closure")] + let is_string = rhs_val.is::(); - let rhs_val = if is_string { - self.get_interned_string( - rhs_val.into_immutable_string().expect("`ImmutableString`"), - ) - .into() - } else { - rhs_val.flatten() - }; - - let _new_val = &mut Some((rhs_val, op_info)); - - // Must be either `var[index] op= val` or `var.prop op= val` - match lhs { - // name op= rhs (handled above) - Expr::Variable(..) => { - unreachable!("Expr::Variable case is already handled") - } - // idx_lhs[idx_expr] op= rhs - #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ) - .map(|_| Dynamic::UNIT), - // dot_lhs.dot_rhs op= rhs - #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ) - .map(|_| Dynamic::UNIT), - _ => unreachable!("cannot assign to expression: {:?}", lhs), - } + let rhs_val = if is_string { + self.get_interned_string( + rhs_val.into_immutable_string().expect("`ImmutableString`"), + ) + .into() } else { - rhs_result + rhs_val.flatten() + }; + + let _new_val = &mut Some((rhs_val, op_info)); + + // Must be either `var[index] op= val` or `var.prop op= val` + return match lhs { + // name op= rhs (handled above) + Expr::Variable(..) => { + unreachable!("Expr::Variable case is already handled") + } + // idx_lhs[idx_expr] op= rhs + #[cfg(not(feature = "no_index"))] + Expr::Index(..) => self.eval_dot_index_chain( + global, caches, lib, level, scope, this_ptr, lhs, _new_val, + ), + // dot_lhs.dot_rhs op= rhs + #[cfg(not(feature = "no_object"))] + Expr::Dot(..) => self.eval_dot_index_chain( + global, caches, lib, level, scope, this_ptr, lhs, _new_val, + ), + _ => unreachable!("cannot assign to expression: {:?}", lhs), } - }; - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - return result; + .map(|_| Dynamic::UNIT); + } } self.track_operation(global, stmt.position())?; - let result = match stmt { + match stmt { // No-op Stmt::Noop(..) => Ok(Dynamic::UNIT), @@ -385,100 +365,69 @@ impl Engine { }, ) = &**x; - let value_result = - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr); + let mut result = None; - if let Ok(value) = value_result { - let expr_result = if value.is_hashable() { - let hasher = &mut get_hasher(); - value.hash(hasher); - let hash = hasher.finish(); + let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; - // First check hashes - if let Some(case_blocks_list) = cases.get(&hash) { - assert!(!case_blocks_list.is_empty()); + if value.is_hashable() { + let hasher = &mut get_hasher(); + value.hash(hasher); + let hash = hasher.finish(); - let mut result = Ok(None); + // First check hashes + if let Some(case_blocks_list) = cases.get(&hash) { + assert!(!case_blocks_list.is_empty()); - for &index in case_blocks_list { - let block = &expressions[index]; + for &index in case_blocks_list { + let block = &expressions[index]; - let cond_result = match block.condition { - Expr::BoolConstant(b, ..) => Ok(b), - ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::( - typ, - c.position(), - ) - }) - }), - }; + let cond_result = match block.condition { + Expr::BoolConstant(b, ..) => b, + ref c => self + .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .as_bool() + .map_err(|typ| { + self.make_type_mismatch_err::(typ, c.position()) + })?, + }; - match cond_result { - Ok(true) => result = Ok(Some(&block.expr)), - Ok(false) => continue, - _ => result = cond_result.map(|_| None), - } + if cond_result { + result = Some(&block.expr); break; } - - result - } else if value.is::() && !ranges.is_empty() { - // Then check integer ranges - let value = value.as_int().expect("`INT`"); - let mut result = Ok(None); - - for r in ranges.iter().filter(|r| r.contains(value)) { - let block = &expressions[r.index()]; - - let cond_result = match block.condition { - Expr::BoolConstant(b, ..) => Ok(b), - ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::( - typ, - c.position(), - ) - }) - }), - }; - - match cond_result { - Ok(true) => result = Ok(Some(&block.expr)), - Ok(false) => continue, - _ => result = cond_result.map(|_| None), - } - break; - } - - result - } else { - // Nothing matches - Ok(None) } - } else { - // Non-hashable - Ok(None) - }; + } else if value.is::() && !ranges.is_empty() { + // Then check integer ranges + let value = value.as_int().expect("`INT`"); - if let Ok(Some(expr)) = expr_result { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) - } else if let Ok(None) = expr_result { - // Default match clause - def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { - let def_expr = &expressions[index].expr; - self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) - }) - } else { - expr_result.map(|_| Dynamic::UNIT) + for r in ranges.iter().filter(|r| r.contains(value)) { + let block = &expressions[r.index()]; + + let cond_result = match block.condition { + Expr::BoolConstant(b, ..) => b, + ref c => self + .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .as_bool() + .map_err(|typ| { + self.make_type_mismatch_err::(typ, c.position()) + })?, + }; + + if cond_result { + result = Some(&block.expr); + break; + } + } } + } + + if let Some(expr) = result { + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) } else { - value_result + def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { + let def_expr = &expressions[index].expr; + self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) + }) } } @@ -512,29 +461,24 @@ impl Engine { loop { let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; - match condition { - Ok(false) => break Ok(Dynamic::UNIT), - Ok(true) if body.is_empty() => (), - Ok(true) => { - match self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, value, ..) => break Ok(value), - _ => break Err(err), - }, + if !condition { + break Ok(Dynamic::UNIT); + } + + if !body.is_empty() { + if let Err(err) = self.eval_stmt_block( + global, caches, lib, level, scope, this_ptr, body, true, + ) { + match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => break Ok(value), + _ => break Err(err), } } - err => break err.map(|_| Dynamic::UNIT), } } } @@ -546,30 +490,24 @@ impl Engine { loop { if !body.is_empty() { - match self.eval_stmt_block( + if let Err(err) = self.eval_stmt_block( global, caches, lib, level, scope, this_ptr, body, true, ) { - Ok(_) => (), - Err(err) => match *err { + match *err { ERR::LoopBreak(false, ..) => continue, ERR::LoopBreak(true, value, ..) => break Ok(value), _ => break Err(err), - }, + } } } let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; - match condition { - Ok(condition) if condition ^ is_while => break Ok(Dynamic::UNIT), - Ok(_) => (), - err => break err.map(|_| Dynamic::UNIT), + if condition ^ is_while { + break Ok(Dynamic::UNIT); } } } @@ -578,102 +516,99 @@ impl Engine { Stmt::For(x, ..) => { let (var_name, counter, expr, statements) = &**x; - let iter_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .map(Dynamic::flatten); + let iter_obj = self + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .flatten(); - if let Ok(iter_obj) = iter_result { - let iter_type = iter_obj.type_id(); + let iter_type = iter_obj.type_id(); - // lib should only contain scripts, so technically they cannot have iterators + // lib should only contain scripts, so technically they cannot have iterators - // Search order: - // 1) Global namespace - functions registered via Engine::register_XXX - // 2) Global modules - packages - // 3) Imported modules - functions marked with global namespace - // 4) Global sub-modules - functions marked with global namespace - let func = self - .global_modules - .iter() - .find_map(|m| m.get_iter(iter_type)); + // Search order: + // 1) Global namespace - functions registered via Engine::register_XXX + // 2) Global modules - packages + // 3) Imported modules - functions marked with global namespace + // 4) Global sub-modules - functions marked with global namespace + let func = self + .global_modules + .iter() + .find_map(|m| m.get_iter(iter_type)); - #[cfg(not(feature = "no_module"))] - let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| { - self.global_sub_modules - .values() - .find_map(|m| m.get_qualified_iter(iter_type)) + #[cfg(not(feature = "no_module"))] + let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| { + self.global_sub_modules + .values() + .find_map(|m| m.get_qualified_iter(iter_type)) + }); + + if let Some(func) = func { + // Restore scope at end of statement + let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::new(scope, move |s| { + s.rewind(orig_scope_len); }); - if let Some(func) = func { - // Add the loop variables - let orig_scope_len = scope.len(); - let counter_index = if counter.is_empty() { - usize::MAX - } else { - scope.push(counter.name.clone(), 0 as INT); - scope.len() - 1 - }; - - scope.push(var_name.name.clone(), ()); - let index = scope.len() - 1; - - let loop_result = func(iter_obj) - .enumerate() - .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - return Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => return Err(err.fill_position(expr.position())), - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - self.track_operation(global, statements.position())?; - - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ) - .map(|_| Dynamic::UNIT) - .or_else(|err| match *err { - ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), - _ => Err(err), - }) - }) - .or_else(|err| match *err { - ERR::LoopBreak(true, value, ..) => Ok(value), - _ => Err(err), - }); - - scope.rewind(orig_scope_len); - - loop_result + // Add the loop variables + let counter_index = if counter.is_empty() { + usize::MAX } else { - Err(ERR::ErrorFor(expr.start_position()).into()) - } + scope.push(counter.name.clone(), 0 as INT); + scope.len() - 1 + }; + + scope.push(var_name.name.clone(), ()); + let index = scope.len() - 1; + + func(iter_obj) + .enumerate() + .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; + + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); + } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + let value = match iter_value { + Ok(v) => v.flatten(), + Err(err) => return Err(err.fill_position(expr.position())), + }; + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + + self.eval_stmt_block( + global, caches, lib, level, scope, this_ptr, statements, true, + ) + .map(|_| Dynamic::UNIT) + .or_else(|err| match *err { + ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), + _ => Err(err), + }) + }) + .or_else(|err| match *err { + ERR::LoopBreak(true, value, ..) => Ok(value), + _ => Err(err), + }) } else { - iter_result + Err(ERR::ErrorFor(expr.start_position()).into()) } } @@ -700,12 +635,12 @@ impl Engine { catch_block, } = &**x; - let result = self + match self .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) - .map(|_| Dynamic::UNIT); - - match result { - Ok(_) => result, + { + Ok(_) => self.eval_stmt_block( + global, caches, lib, level, scope, this_ptr, try_block, true, + ), Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), Err(mut err) => { @@ -744,13 +679,18 @@ impl Engine { } }; + // Restore scope at end of block let orig_scope_len = scope.len(); + let scope = + &mut *RestoreOnDrop::new_if(!catch_var.is_empty(), scope, move |s| { + s.rewind(orig_scope_len); + }); if !catch_var.is_empty() { scope.push(catch_var.clone(), err_value); } - let result = self.eval_stmt_block( + self.eval_stmt_block( global, caches, lib, @@ -759,21 +699,16 @@ impl Engine { this_ptr, catch_block, true, - ); - - scope.rewind(orig_scope_len); - - match result { - Ok(_) => Ok(Dynamic::UNIT), - Err(result_err) => match *result_err { - // Re-throw exception - ERR::ErrorRuntime(Dynamic(Union::Unit(..)), pos) => { - err.set_position(pos); - Err(err) - } - _ => Err(result_err), - }, - } + ) + .map(|_| Dynamic::UNIT) + .map_err(|result_err| match *result_err { + // Re-throw exception + ERR::ErrorRuntime(Dynamic(Union::Unit(..)), pos) => { + err.set_position(pos); + err + } + _ => result_err, + }) } } } @@ -812,7 +747,7 @@ impl Engine { let export = options.contains(ASTFlags::EXPORTED); // Check variable definition filter - let result = if let Some(ref filter) = self.def_var_filter { + if let Some(ref filter) = self.def_var_filter { let will_shadow = scope.contains(var_name); let nesting_level = global.scope_level; let is_const = access == AccessMode::ReadOnly; @@ -824,74 +759,56 @@ impl Engine { }; let context = EvalContext::new(self, global, None, lib, level, scope, this_ptr); - match filter(true, info, context) { - Ok(true) => None, - Ok(false) => { - Some(Err( - ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into() - )) - } - err @ Err(_) => Some(err), + if !filter(true, info, context)? { + return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); } + } + + // Evaluate initial value + let mut value = self + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .flatten(); + + let _alias = if !rewind_scope { + // Put global constants into global module + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_module"))] + if global.scope_level == 0 + && access == AccessMode::ReadOnly + && lib.iter().any(|m| !m.is_empty()) + { + crate::func::locked_write(global.constants.get_or_insert_with(|| { + crate::Shared::new( + crate::Locked::new(std::collections::BTreeMap::new()), + ) + })) + .insert(var_name.name.clone(), value.clone()); + } + + if export { + Some(var_name) + } else { + None + } + } else if export { + unreachable!("exported variable not on global level"); } else { None }; - if let Some(result) = result { - result.map(|_| Dynamic::UNIT) + if let Some(index) = index { + value.set_access_mode(access); + *scope.get_mut_by_index(scope.len() - index.get()) = value; } else { - // Evaluate initial value - let value_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .map(Dynamic::flatten); - - if let Ok(mut value) = value_result { - let _alias = if !rewind_scope { - // Put global constants into global module - #[cfg(not(feature = "no_function"))] - #[cfg(not(feature = "no_module"))] - if global.scope_level == 0 - && access == AccessMode::ReadOnly - && lib.iter().any(|m| !m.is_empty()) - { - crate::func::locked_write(global.constants.get_or_insert_with( - || { - crate::Shared::new(crate::Locked::new( - std::collections::BTreeMap::new(), - )) - }, - )) - .insert(var_name.name.clone(), value.clone()); - } - - if export { - Some(var_name) - } else { - None - } - } else if export { - unreachable!("exported variable not on global level"); - } else { - None - }; - - if let Some(index) = index { - value.set_access_mode(access); - *scope.get_mut_by_index(scope.len() - index.get()) = value; - } else { - scope.push_entry(var_name.name.clone(), access, value); - } - - #[cfg(not(feature = "no_module"))] - if let Some(alias) = _alias { - scope.add_alias_by_index(scope.len() - 1, alias.name.as_str().into()); - } - - Ok(Dynamic::UNIT) - } else { - value_result - } + scope.push_entry(var_name.name.clone(), access, value); } + + #[cfg(not(feature = "no_module"))] + if let Some(alias) = _alias { + scope.add_alias_by_index(scope.len() - 1, alias.name.as_str().into()); + } + + Ok(Dynamic::UNIT) } // Import statement @@ -904,66 +821,52 @@ impl Engine { return Err(ERR::ErrorTooManyModules(*_pos).into()); } - let path_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - let typ = v.type_name(); - v.try_cast::().ok_or_else(|| { - self.make_type_mismatch_err::( - typ, - expr.position(), - ) - }) - }); + let v = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let typ = v.type_name(); + let path = v.try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(typ, expr.position()) + })?; - if let Ok(path) = path_result { - use crate::ModuleResolver; + use crate::ModuleResolver; - let path_pos = expr.start_position(); + let path_pos = expr.start_position(); - let resolver = global.embedded_module_resolver.clone(); + let resolver = global.embedded_module_resolver.clone(); - let module_result = resolver - .as_ref() - .and_then(|r| match r.resolve_raw(self, global, &path, path_pos) { - Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None, - result => Some(result), - }) - .or_else(|| { - Some( - self.module_resolver - .resolve_raw(self, global, &path, path_pos), - ) - }) - .unwrap_or_else(|| { - Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) - }); + let module = resolver + .as_ref() + .and_then(|r| match r.resolve_raw(self, global, &path, path_pos) { + Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None, + result => Some(result), + }) + .or_else(|| { + Some( + self.module_resolver + .resolve_raw(self, global, &path, path_pos), + ) + }) + .unwrap_or_else(|| { + Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) + })?; - if let Ok(module) = module_result { - let (export, must_be_indexed) = if !export.is_empty() { - (export.name.clone(), true) - } else { - (self.get_interned_string(""), false) - }; - - if !must_be_indexed || module.is_indexed() { - global.push_import(export, module); - } else { - // Index the module (making a clone copy if necessary) if it is not indexed - let mut m = crate::func::shared_take_or_clone(module); - m.build_index(); - global.push_import(export, m); - } - - global.num_modules_loaded += 1; - - Ok(Dynamic::UNIT) - } else { - module_result.map(|_| Dynamic::UNIT) - } + let (export, must_be_indexed) = if !export.is_empty() { + (export.name.clone(), true) } else { - path_result.map(|_| Dynamic::UNIT) + (self.get_interned_string(""), false) + }; + + if !must_be_indexed || module.is_indexed() { + global.push_import(export, module); + } else { + // Index the module (making a clone copy if necessary) if it is not indexed + let mut m = crate::func::shared_take_or_clone(module); + m.build_index(); + global.push_import(export, m); } + + global.num_modules_loaded += 1; + + Ok(Dynamic::UNIT) } // Export statement @@ -1004,12 +907,7 @@ impl Engine { } _ => unreachable!("statement cannot be evaluated: {:?}", stmt), - }; - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result + } } /// Evaluate a list of statements with no `this` pointer. diff --git a/src/func/call.rs b/src/func/call.rs index ff95e3a4..6c7ddf75 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -9,6 +9,7 @@ use crate::engine::{ }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; +use crate::types::RestoreOnDrop; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, @@ -88,9 +89,11 @@ impl<'a> ArgBackup<'a> { /// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_ /// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak. #[inline(always)] - pub fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) { + pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) { if let Some(p) = self.orig_mut.take() { args[0] = p; + } else { + unreachable!("`Some`"); } } } @@ -327,7 +330,7 @@ impl Engine { name: &str, op_token: Option<&Token>, hash: u64, - args: &mut FnCallArgs, + mut args: &mut FnCallArgs, is_ref_mut: bool, pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { @@ -354,48 +357,45 @@ impl Engine { #[cfg(feature = "debugging")] let orig_call_stack_len = global.debugger.call_stack().len(); - let mut _result = if let Some(FnResolutionCacheEntry { func, source }) = func { - assert!(func.is_native()); + let FnResolutionCacheEntry { func, source } = func.unwrap(); + assert!(func.is_native()); - let mut backup = ArgBackup::new(); + let backup = &mut ArgBackup::new(); - // Calling pure function but the first argument is a reference? - if is_ref_mut && func.is_pure() && !args.is_empty() { - // Clone the first argument - backup.change_first_arg_to_copy(args); - } + // Calling pure function but the first argument is a reference? + let swap = is_ref_mut && func.is_pure() && !args.is_empty(); - #[cfg(feature = "debugging")] - if self.debugger.is_some() { - global.debugger.push_call_stack_frame( - self.get_interned_string(name), - args.iter().map(|v| (*v).clone()).collect(), - source.clone().or_else(|| global.source.clone()), - pos, - ); - } + if swap { + // Clone the first argument + backup.change_first_arg_to_copy(args); + } - // Run external function - let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, lib, pos, level).into(); + let args = + &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| backup.restore_first_arg(a)); - let result = if func.is_plugin_fn() { - let f = func.get_plugin_fn().unwrap(); - if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { - Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) - } else { - f.call(context, args) - } + #[cfg(feature = "debugging")] + if self.debugger.is_some() { + global.debugger.push_call_stack_frame( + self.get_interned_string(name), + args.iter().map(|v| (*v).clone()).collect(), + source.clone().or_else(|| global.source.clone()), + pos, + ); + } + + // Run external function + let src = source.as_ref().map(|s| s.as_str()); + let context = (self, name, src, &*global, lib, pos, level).into(); + + let mut _result = if func.is_plugin_fn() { + let f = func.get_plugin_fn().unwrap(); + if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { + Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) } else { - func.get_native_fn().unwrap()(context, args) - }; - - // Restore the original reference - backup.restore_first_arg(args); - - result + f.call(context, args) + } } else { - unreachable!("`Some`"); + func.get_native_fn().unwrap()(context, args) }; #[cfg(feature = "debugging")] @@ -413,11 +413,11 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self + + if let Err(err) = self .run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) { - Ok(_) => (), - Err(err) => _result = Err(err), + _result = Err(err); } } @@ -543,7 +543,7 @@ impl Engine { fn_name: &str, op_token: Option<&Token>, hashes: FnCallHashes, - args: &mut FnCallArgs, + mut args: &mut FnCallArgs, is_ref_mut: bool, _is_method_call: bool, pos: Position, @@ -613,19 +613,11 @@ impl Engine { #[cfg(not(feature = "no_function"))] if !hashes.is_native_only() { // Script-defined function call? + let hash = hashes.script(); let local_entry = &mut None; if let Some(FnResolutionCacheEntry { func, ref source }) = self - .resolve_fn( - global, - caches, - local_entry, - lib, - None, - hashes.script(), - None, - false, - ) + .resolve_fn(global, caches, local_entry, lib, None, hash, None, false) .cloned() { // Script function call @@ -647,8 +639,9 @@ impl Engine { }; let orig_source = mem::replace(&mut global.source, source.clone()); + let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); - let result = if _is_method_call { + return if _is_method_call { // Method call of script function - map first argument to `this` let (first_arg, rest_args) = args.split_first_mut().unwrap(); @@ -666,27 +659,24 @@ impl Engine { ) } else { // Normal call of script function - let mut backup = ArgBackup::new(); + let backup = &mut ArgBackup::new(); // The first argument is a reference? - if is_ref_mut && !args.is_empty() { + let swap = is_ref_mut && !args.is_empty(); + + if swap { backup.change_first_arg_to_copy(args); } - let result = self.call_script_fn( + let args = &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| { + backup.restore_first_arg(a) + }); + + self.call_script_fn( global, caches, lib, level, scope, &mut None, func, args, true, pos, - ); - - // Restore the original reference - backup.restore_first_arg(args); - - result - }; - - // Restore the original source - global.source = orig_source; - - return result.map(|r| (r, false)); + ) + } + .map(|r| (r, false)); } } @@ -722,17 +712,14 @@ impl Engine { // Do not match function exit for arguments #[cfg(feature = "debugging")] - let reset_debugger = global.debugger.clear_status_if(|status| { + let reset = global.debugger.clear_status_if(|status| { matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) }); - - let result = self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr); - - // Restore function exit status #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); - result.map(|r| (r, arg_expr.start_position())) + self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) + .map(|r| (r, arg_expr.start_position())) } /// Call a dot method. @@ -1389,15 +1376,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 orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); + let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); - let result = self.call_script_fn( + self.call_script_fn( global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, - ); - - global.source = orig_source; - - result + ) } Some(f) if f.is_plugin_fn() => { diff --git a/src/types/interner.rs b/src/types/interner.rs index 4b7d387e..43a2d395 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,3 +1,5 @@ +//! A strings interner type. + use super::BloomFilterU64; use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::ImmutableString; @@ -20,10 +22,8 @@ pub const MAX_INTERNED_STRINGS: usize = 1024; /// Maximum length of strings interned. pub const MAX_STRING_LEN: usize = 24; -/// _(internals)_ A factory of identifiers from text strings. +/// _(internals)_ A cache for interned strings. /// Exported under the `internals` feature only. -/// -/// Normal identifiers, property getters and setters are interned separately. pub struct StringsInterner<'a> { /// Maximum number of strings interned. pub capacity: usize, @@ -103,43 +103,48 @@ impl StringsInterner<'_> { if value.strong_count() > 1 { return value; } - e.insert(value).clone() } }; - // If the interner is over capacity, remove the longest entry that has the lowest count - if self.cache.len() > self.capacity { - // Throttle: leave some buffer to grow when shrinking the cache. - // We leave at least two entries, one for the empty string, and one for the string - // that has just been inserted. - let max = if self.capacity < 5 { - 2 - } else { - self.capacity - 3 - }; - - while self.cache.len() > max { - let (_, _, n) = self - .cache - .iter() - .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { - if k != hash - && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) - { - (v.len(), v.strong_count(), k) - } else { - (x, c, n) - } - }); - - self.cache.remove(&n); - } - } + // Throttle the cache upon exit + self.throttle_cache(hash); result } + /// If the interner is over capacity, remove the longest entry that has the lowest count + fn throttle_cache(&mut self, hash: u64) { + if self.cache.len() <= self.capacity { + return; + } + + // Leave some buffer to grow when shrinking the cache. + // We leave at least two entries, one for the empty string, and one for the string + // that has just been inserted. + let max = if self.capacity < 5 { + 2 + } else { + self.capacity - 3 + }; + + while self.cache.len() > max { + let (_, _, n) = self + .cache + .iter() + .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { + if k != hash && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) + { + (v.len(), v.strong_count(), k) + } else { + (x, c, n) + } + }); + + self.cache.remove(&n); + } + } + /// Number of strings interned. #[inline(always)] #[must_use] diff --git a/src/types/mod.rs b/src/types/mod.rs index b6ca3802..1fbf33d3 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -8,6 +8,7 @@ pub mod fn_ptr; pub mod immutable_string; pub mod interner; pub mod parse_error; +pub mod restore; pub mod scope; pub mod variant; @@ -21,5 +22,6 @@ pub use fn_ptr::FnPtr; pub use immutable_string::ImmutableString; pub use interner::StringsInterner; pub use parse_error::{LexError, ParseError, ParseErrorType}; +pub use restore::RestoreOnDrop; pub use scope::Scope; pub use variant::Variant; diff --git a/src/types/restore.rs b/src/types/restore.rs new file mode 100644 index 00000000..d2e6cd87 --- /dev/null +++ b/src/types/restore.rs @@ -0,0 +1,57 @@ +//! Facility to run state restoration logic at the end of scope. + +use std::ops::{Deref, DerefMut}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +/// Run custom restoration logic upon the end of scope. +#[must_use] +pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> { + value: &'a mut T, + restore: Option, +} + +impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> { + /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope only when + /// `need_restore` is `true`. + #[inline(always)] + pub fn new_if(need_restore: bool, value: &'a mut T, restore: R) -> Self { + Self { + value, + restore: if need_restore { Some(restore) } else { None }, + } + } + /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope. + #[inline(always)] + pub fn new(value: &'a mut T, restore: R) -> Self { + Self { + value, + restore: Some(restore), + } + } +} + +impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> { + #[inline(always)] + fn drop(&mut self) { + if let Some(restore) = self.restore.take() { + restore(self.value); + } + } +} + +impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.value + } +} + +impl<'a, T, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.value + } +} From 6053aa164158bb95686d3403cb51743fc794f7be Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 15:01:40 +0800 Subject: [PATCH 111/116] Clean up types. --- src/api/call_fn.rs | 8 +- src/api/eval.rs | 8 +- src/api/register.rs | 11 ++- src/api/run.rs | 8 +- src/ast/ast.rs | 22 ++--- src/ast/script_fn.rs | 4 +- src/engine.rs | 7 +- src/eval/chaining.rs | 26 +++--- src/eval/debugger.rs | 13 ++- src/eval/eval_context.rs | 23 ++--- src/eval/expr.rs | 23 +++-- src/eval/global_state.rs | 24 +++-- src/eval/stmt.rs | 144 ++++++++++++++--------------- src/func/call.rs | 36 ++++---- src/func/native.rs | 22 ++--- src/func/script.rs | 10 +- src/lib.rs | 3 + src/module/mod.rs | 10 +- src/module/resolvers/collection.rs | 4 +- src/module/resolvers/dummy.rs | 4 +- src/module/resolvers/file.rs | 13 +-- src/module/resolvers/mod.rs | 6 +- src/module/resolvers/stat.rs | 23 ++--- src/optimizer.rs | 6 +- src/packages/mod.rs | 4 +- src/parser.rs | 10 +- src/types/fn_ptr.rs | 6 +- src/types/restore.rs | 18 +++- 28 files changed, 248 insertions(+), 248 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 2057d9b7..b467e54d 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -5,8 +5,8 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::types::RestoreOnDrop; use crate::{ - reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, - AST, ERR, + reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, + StaticVec, AST, ERR, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -249,7 +249,7 @@ impl Engine { arg_values: &mut [Dynamic], ) -> RhaiResult { let statements = ast.statements(); - let lib = &[AsRef::>::as_ref(ast).clone()]; + let lib = &[AsRef::::as_ref(ast).clone()]; let mut this_ptr = this_ptr; let orig_scope_len = scope.len(); @@ -260,7 +260,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); diff --git a/src/api/eval.rs b/src/api/eval.rs index 294bf89a..28bcdcbd 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -196,7 +196,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; @@ -227,7 +227,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); @@ -239,7 +239,7 @@ impl Engine { let lib = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; self.eval_global_statements(global, caches, lib, level, scope, statements) @@ -258,7 +258,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[crate::Shared], + lib: &[crate::SharedModule], level: usize, scope: &mut Scope, statements: &[crate::ast::Stmt], diff --git a/src/api/register.rs b/src/api/register.rs index ce5c0c4a..9e2f7490 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -4,6 +4,7 @@ use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::types::dynamic::Variant; use crate::{ Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared, + SharedModule, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -636,7 +637,7 @@ impl Engine { /// When searching for functions, modules loaded later are preferred. In other words, loaded /// modules are searched in reverse order. #[inline(always)] - pub fn register_global_module(&mut self, module: Shared) -> &mut Self { + pub fn register_global_module(&mut self, module: SharedModule) -> &mut Self { // Insert the module into the front. // The first module is always the global namespace. self.global_modules.insert(1, module); @@ -661,7 +662,7 @@ impl Engine { /// let mut module = Module::new(); /// module.set_native_fn("calc", |x: i64| Ok(x + 1)); /// - /// let module: Shared = module.into(); + /// let module: SharedModule = module.into(); /// /// engine /// // Register the module as a fixed sub-module @@ -680,12 +681,12 @@ impl Engine { pub fn register_static_module( &mut self, name: impl AsRef, - module: Shared, + module: SharedModule, ) -> &mut Self { fn register_static_module_raw( - root: &mut std::collections::BTreeMap>, + root: &mut std::collections::BTreeMap, name: &str, - module: Shared, + module: SharedModule, ) { let separator = crate::tokenizer::Token::DoubleColon.syntax(); let separator = separator.as_ref(); diff --git a/src/api/run.rs b/src/api/run.rs index 34f1d106..90c6abf2 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,7 +2,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; -use crate::{Engine, RhaiResultOf, Scope, AST}; +use crate::{Engine, RhaiResultOf, Scope, SharedModule, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -122,9 +122,9 @@ impl Engine { let statements = ast.statements(); if !statements.is_empty() { - let lib: &[crate::Shared] = &[ + let lib: &[SharedModule] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { &[][..] @@ -139,7 +139,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 9c186e67..76277e7b 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -28,7 +28,7 @@ pub struct AST { body: StmtBlock, /// Script-defined functions. #[cfg(not(feature = "no_function"))] - lib: crate::Shared, + lib: crate::SharedModule, /// Embedded module resolver, if any. #[cfg(not(feature = "no_module"))] resolver: Option>, @@ -74,7 +74,7 @@ impl AST { #[must_use] pub(crate) fn new( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, ) -> Self { Self { source: None, @@ -94,7 +94,7 @@ impl AST { #[must_use] pub fn new( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, ) -> Self { Self { source: None, @@ -113,7 +113,7 @@ impl AST { #[must_use] pub(crate) fn new_with_source( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, source: impl Into, ) -> Self { let mut ast = Self::new( @@ -131,7 +131,7 @@ impl AST { #[must_use] pub fn new_with_source( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, source: impl Into, ) -> Self { let mut ast = Self::new( @@ -267,7 +267,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub(crate) const fn shared_lib(&self) -> &crate::Shared { + pub(crate) const fn shared_lib(&self) -> &SharedModule { &self.lib } /// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions. @@ -278,7 +278,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub const fn shared_lib(&self) -> &crate::Shared { + pub const fn shared_lib(&self) -> &crate::SharedModule { &self.lib } /// Get the embedded [module resolver][crate::ModuleResolver]. @@ -957,19 +957,19 @@ impl AsRef for AST { } #[cfg(not(feature = "no_function"))] -impl Borrow> for AST { +impl Borrow for AST { #[inline(always)] #[must_use] - fn borrow(&self) -> &crate::Shared { + fn borrow(&self) -> &crate::SharedModule { self.shared_lib() } } #[cfg(not(feature = "no_function"))] -impl AsRef> for AST { +impl AsRef for AST { #[inline(always)] #[must_use] - fn as_ref(&self) -> &crate::Shared { + fn as_ref(&self) -> &crate::SharedModule { self.shared_lib() } } diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index d40d6a5a..743bf2f5 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -20,9 +20,9 @@ use std::{fmt, hash::Hash}; #[derive(Debug, Clone)] pub struct EncapsulatedEnviron { /// Functions defined within the same [`AST`][crate::AST]. - pub lib: crate::Shared, + pub lib: crate::SharedModule, /// Imported [modules][crate::Module]. - pub imports: Box<[(ImmutableString, crate::Shared)]>, + pub imports: Box<[(ImmutableString, crate::SharedModule)]>, /// Globally-defined constants. pub constants: Option, } diff --git a/src/engine.rs b/src/engine.rs index 0afadf75..8d5df823 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,7 +9,8 @@ use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; use crate::types::StringsInterner; use crate::{ - Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Shared, StaticVec, + Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, SharedModule, + StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -91,10 +92,10 @@ pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax(); /// ``` pub struct Engine { /// A collection of all modules loaded into the global namespace of the Engine. - pub(crate) global_modules: StaticVec>, + pub(crate) global_modules: StaticVec, /// A collection of all sub-modules directly loaded into the Engine. #[cfg(not(feature = "no_module"))] - pub(crate) global_sub_modules: std::collections::BTreeMap>, + pub(crate) global_sub_modules: std::collections::BTreeMap, /// A module resolution service. #[cfg(not(feature = "no_module"))] diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 7d7560fc..021dfdc3 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -6,7 +6,7 @@ use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; use crate::types::RestoreOnDrop; use crate::{ - Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, + Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, }; use std::hash::Hash; #[cfg(feature = "no_std")] @@ -43,7 +43,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, @@ -205,7 +205,7 @@ impl Engine { global, caches, lib, level, scope, this_ptr, rhs, )?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) }); @@ -216,7 +216,7 @@ impl Engine { // Truncate the index values upon exit let offset = idx_values.len() - args.len(); let idx_values = - &mut *RestoreOnDrop::new(idx_values, move |v| v.truncate(offset)); + &mut *RestoreOnDrop::lock(idx_values, move |v| v.truncate(offset)); let call_args = &mut idx_values[offset..]; let pos1 = args.get(0).map_or(Position::NONE, Expr::position); @@ -384,7 +384,7 @@ impl Engine { global, caches, lib, level, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) }); @@ -394,7 +394,7 @@ impl Engine { // Truncate the index values upon exit let offset = idx_values.len() - args.len(); - let idx_values = &mut *RestoreOnDrop::new(idx_values, move |v| { + let idx_values = &mut *RestoreOnDrop::lock(idx_values, move |v| { v.truncate(offset) }); @@ -516,7 +516,7 @@ impl Engine { global, caches, lib, level, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) }); @@ -527,7 +527,7 @@ impl Engine { // Truncate the index values upon exit let offset = idx_values.len() - args.len(); let idx_values = - &mut *RestoreOnDrop::new(idx_values, move |v| { + &mut *RestoreOnDrop::lock(idx_values, move |v| { v.truncate(offset) }); @@ -570,7 +570,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -661,7 +661,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -773,7 +773,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -796,7 +796,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -820,7 +820,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 3048e5bc..8c2d94e5 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{ - Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, Shared, + Dynamic, Engine, EvalAltResult, ImmutableString, Position, RhaiResultOf, Scope, SharedModule, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -413,7 +413,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -440,7 +440,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -463,7 +463,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -510,7 +510,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -519,8 +519,7 @@ impl Engine { ) -> Result, Box> { let src = global.source_raw().cloned(); let src = src.as_ref().map(|s| s.as_str()); - let context = - crate::EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); + let context = crate::EvalContext::new(self, global, caches, lib, level, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index 99456ce1..dc50a63c 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -1,7 +1,7 @@ //! Evaluation context. use super::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Scope, Shared}; +use crate::{Dynamic, Engine, Module, Scope, SharedModule}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -16,9 +16,9 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current [`GlobalRuntimeState`]. global: &'g mut GlobalRuntimeState, /// The current [caches][Caches], if available. - caches: Option<&'c mut Caches>, + caches: &'c mut Caches, /// The current stack of imported [modules][Module]. - lib: &'a [Shared], + lib: &'a [SharedModule], /// The current bound `this` pointer, if any. this_ptr: &'t mut Option<&'pt mut Dynamic>, /// The current nesting level of function calls. @@ -32,8 +32,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { pub fn new( engine: &'a Engine, global: &'g mut GlobalRuntimeState, - caches: Option<&'c mut Caches>, - lib: &'a [Shared], + caches: &'c mut Caches, + lib: &'a [SharedModule], level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Option<&'pt mut Dynamic>, @@ -117,7 +117,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[Shared] { + pub const fn namespaces(&self) -> &[SharedModule] { self.lib } /// The current bound `this` pointer, if any. @@ -173,17 +173,10 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { ) -> crate::RhaiResult { let expr: &crate::ast::Expr = expr; - let mut new_caches = Caches::new(); - - let caches = match self.caches.as_mut() { - Some(c) => c, - None => &mut new_caches, - }; - match expr { crate::ast::Expr::Stmt(statements) => self.engine.eval_stmt_block( self.global, - caches, + self.caches, self.lib, self.level, self.scope, @@ -193,7 +186,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { ), _ => self.engine.eval_expr( self.global, - caches, + self.caches, self.lib, self.level, self.scope, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 269ab992..89f0d4a7 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -4,8 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; -use crate::types::RestoreOnDrop; -use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR}; +use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -19,7 +18,7 @@ impl Engine { &self, global: &GlobalRuntimeState, namespace: &crate::ast::Namespace, - ) -> Option> { + ) -> Option { assert!(!namespace.is_empty()); let root = namespace.root(); @@ -52,7 +51,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -138,7 +137,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -174,7 +173,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, level, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -222,7 +221,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -238,7 +237,9 @@ impl Engine { let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { + g.debugger.reset_status(reset) + }); self.track_operation(global, expr.position())?; @@ -269,7 +270,9 @@ impl Engine { let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { + g.debugger.reset_status(reset) + }); self.track_operation(global, expr.position())?; @@ -418,7 +421,7 @@ impl Engine { )) })?; let mut context = - EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); + EvalContext::new(self, global, caches, lib, level, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 935434ef..413dee13 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -28,7 +28,7 @@ pub struct GlobalRuntimeState { imports: crate::StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec>, + modules: crate::StaticVec, /// Source of the current context. /// /// No source if the string is empty. @@ -127,7 +127,7 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn get_shared_import(&self, index: usize) -> Option> { + pub fn get_shared_import(&self, index: usize) -> Option { self.modules.get(index).cloned() } /// Get a mutable reference to the globally-imported [module][crate::Module] at a @@ -141,7 +141,7 @@ impl GlobalRuntimeState { pub(crate) fn get_shared_import_mut( &mut self, index: usize, - ) -> Option<&mut crate::Shared> { + ) -> Option<&mut crate::SharedModule> { self.modules.get_mut(index) } /// Get the index of a globally-imported [module][crate::Module] by name. @@ -165,7 +165,7 @@ impl GlobalRuntimeState { pub fn push_import( &mut self, name: impl Into, - module: impl Into>, + module: impl Into, ) { self.imports.push(name.into()); self.modules.push(module.into()); @@ -198,7 +198,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. @@ -208,7 +208,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 @@ -316,11 +316,11 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] impl IntoIterator for GlobalRuntimeState { - type Item = (ImmutableString, crate::Shared); + type Item = (ImmutableString, crate::SharedModule); type IntoIter = std::iter::Rev< std::iter::Zip< smallvec::IntoIter<[ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, - smallvec::IntoIter<[crate::Shared; crate::STATIC_VEC_INLINE_SIZE]>, + smallvec::IntoIter<[crate::SharedModule; crate::STATIC_VEC_INLINE_SIZE]>, >, >; @@ -331,11 +331,11 @@ impl IntoIterator for GlobalRuntimeState { #[cfg(not(feature = "no_module"))] impl<'a> IntoIterator for &'a GlobalRuntimeState { - type Item = (&'a ImmutableString, &'a crate::Shared); + type Item = (&'a ImmutableString, &'a crate::SharedModule); type IntoIter = std::iter::Rev< std::iter::Zip< std::slice::Iter<'a, ImmutableString>, - std::slice::Iter<'a, crate::Shared>, + std::slice::Iter<'a, crate::SharedModule>, >, >; @@ -345,9 +345,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState { } #[cfg(not(feature = "no_module"))] -impl, M: Into>> Extend<(K, M)> - for GlobalRuntimeState -{ +impl, M: Into> Extend<(K, M)> for GlobalRuntimeState { #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 2841ecba..1e232ddc 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -8,10 +8,7 @@ use crate::ast::{ use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; use crate::types::RestoreOnDrop; -use crate::{ - Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, - ERR, INT, -}; +use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, INT}; use std::hash::{Hash, Hasher}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -29,7 +26,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -42,7 +39,7 @@ impl Engine { // Restore scope at end of block if necessary let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::new_if(restore_orig_state, scope, move |s| { + let scope = &mut *RestoreOnDrop::lock_if(restore_orig_state, scope, move |s| { s.rewind(orig_scope_len); }); @@ -55,7 +52,7 @@ impl Engine { global.scope_level += 1; } - let global = &mut *RestoreOnDrop::new_if(restore_orig_state, global, move |g| { + let global = &mut *RestoreOnDrop::lock_if(restore_orig_state, global, move |g| { g.scope_level -= 1; #[cfg(not(feature = "no_module"))] g.truncate_imports(orig_imports_len); @@ -67,7 +64,7 @@ impl Engine { // Pop new function resolution caches at end of block let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); - let caches = &mut *RestoreOnDrop::new(caches, move |c| { + let caches = &mut *RestoreOnDrop::lock(caches, move |c| { c.rewind_fn_resolution_caches(orig_fn_resolution_caches_len) }); @@ -96,10 +93,11 @@ impl Engine { .skip(imports_len) .any(|(.., m)| m.contains_indexed_global_functions()) { + // Different scenarios where the cache must be cleared - notice that this is + // expensive as all function resolutions must start again if caches.fn_resolution_caches_len() > orig_fn_resolution_caches_len { // 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 - // resolutions must start again + // a new cache, just clear it caches.fn_resolution_cache_mut().clear(); } else if restore_orig_state { // When new module is imported with global functions, push a new cache @@ -120,20 +118,18 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, op_info: &OpAssignment, target: &mut Target, root: (&str, Position), - new_val: Dynamic, + mut new_val: Dynamic, ) -> RhaiResultOf<()> { + // Assignment to constant variable? if target.is_read_only() { - // Assignment to constant variable return Err(ERR::ErrorAssignmentToConstant(root.0.to_string(), root.1).into()); } - let mut new_val = new_val; - if op_info.is_op_assignment() { let OpAssignment { hash_op_assign, @@ -204,7 +200,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -215,7 +211,7 @@ impl Engine { let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -240,11 +236,9 @@ impl Engine { .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? .flatten(); - let search_val = + let (mut lhs_ptr, pos) = self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; - let (mut lhs_ptr, pos) = search_val; - let var_name = x.3.as_str(); #[cfg(not(feature = "no_closure"))] @@ -267,16 +261,17 @@ impl Engine { return self .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) .map(|_| Dynamic::UNIT); - } else { - let (op_info, BinaryExpr { lhs, rhs }) = &**x; + } + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + { let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; // Check if the result is a string. If so, intern it. #[cfg(not(feature = "no_closure"))] - let is_string = !rhs_val.is_shared() && rhs_val.is::(); + let is_string = !rhs_val.is_shared() && rhs_val.is::(); #[cfg(feature = "no_closure")] - let is_string = rhs_val.is::(); + let is_string = rhs_val.is::(); let rhs_val = if is_string { self.get_interned_string( @@ -333,23 +328,20 @@ impl Engine { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; - match guard_val { - Ok(true) if if_block.is_empty() => Ok(Dynamic::UNIT), - Ok(true) => self.eval_stmt_block( + if guard_val && !if_block.is_empty() { + self.eval_stmt_block( global, caches, lib, level, scope, this_ptr, if_block, true, - ), - Ok(false) if else_block.is_empty() => Ok(Dynamic::UNIT), - Ok(false) => self.eval_stmt_block( + ) + } else if !guard_val && !else_block.is_empty() { + self.eval_stmt_block( global, caches, lib, level, scope, this_ptr, else_block, true, - ), - err => err.map(Into::into), + ) + } else { + Ok(Dynamic::UNIT) } } @@ -421,14 +413,11 @@ impl Engine { } } - if let Some(expr) = result { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) - } else { - def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { - let def_expr = &expressions[index].expr; - self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) + result + .or_else(|| def_case.as_ref().map(|&index| &expressions[index].expr)) + .map_or(Ok(Dynamic::UNIT), |expr| { + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) }) - } } // Loop @@ -439,17 +428,16 @@ impl Engine { loop { self.track_operation(global, body.position())?; } - } else { - loop { - match self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, value, ..) => break Ok(value), - _ => break Err(err), - }, + } + + loop { + if let Err(err) = self + .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + { + match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => break Ok(value), + _ => break Err(err), } } } @@ -469,15 +457,17 @@ impl Engine { break Ok(Dynamic::UNIT); } - if !body.is_empty() { - if let Err(err) = self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { - match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, value, ..) => break Ok(value), - _ => break Err(err), - } + if body.is_empty() { + continue; + } + + if let Err(err) = self + .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + { + match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => break Ok(value), + _ => break Err(err), } } } @@ -544,7 +534,7 @@ impl Engine { if let Some(func) = func { // Restore scope at end of statement let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::new(scope, move |s| { + let scope = &mut *RestoreOnDrop::lock(scope, move |s| { s.rewind(orig_scope_len); }); @@ -616,12 +606,13 @@ impl Engine { Stmt::BreakLoop(expr, options, pos) => { let is_break = options.contains(ASTFlags::BREAK); - if let Some(ref expr) = expr { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| ERR::LoopBreak(is_break, v, *pos).into()) + let value = if let Some(ref expr) = expr { + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? } else { - Err(ERR::LoopBreak(is_break, Dynamic::UNIT, *pos).into()) - } + Dynamic::UNIT + }; + + Err(ERR::LoopBreak(is_break, value, *pos).into()) } // Try/Catch statement @@ -638,9 +629,7 @@ impl Engine { match self .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) { - Ok(_) => self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, try_block, true, - ), + r @ Ok(_) => r, Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), Err(mut err) => { @@ -682,7 +671,7 @@ impl Engine { // Restore scope at end of block let orig_scope_len = scope.len(); let scope = - &mut *RestoreOnDrop::new_if(!catch_var.is_empty(), scope, move |s| { + &mut *RestoreOnDrop::lock_if(!catch_var.is_empty(), scope, move |s| { s.rewind(orig_scope_len); }); @@ -757,7 +746,8 @@ impl Engine { nesting_level, will_shadow, }; - let context = EvalContext::new(self, global, None, lib, level, scope, this_ptr); + let context = + EvalContext::new(self, global, caches, lib, level, scope, this_ptr); if !filter(true, info, context)? { return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); @@ -917,7 +907,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, statements: &[Stmt], diff --git a/src/func/call.rs b/src/func/call.rs index 6c7ddf75..d7f0102d 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,8 +11,8 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::types::RestoreOnDrop; use crate::{ - calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, SharedModule, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -169,7 +169,7 @@ impl Engine { _global: &GlobalRuntimeState, caches: &'s mut Caches, local_entry: &'s mut Option, - lib: &[Shared], + lib: &[SharedModule], op_token: Option<&Token>, hash_base: u64, args: Option<&mut FnCallArgs>, @@ -325,7 +325,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, name: &str, op_token: Option<&Token>, @@ -371,7 +371,7 @@ impl Engine { } let args = - &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| backup.restore_first_arg(a)); + &mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| backup.restore_first_arg(a)); #[cfg(feature = "debugging")] if self.debugger.is_some() { @@ -537,7 +537,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, _scope: Option<&mut Scope>, fn_name: &str, @@ -585,7 +585,7 @@ impl Engine { } else { let hash_script = calc_fn_hash(None, fn_name.as_str(), num_params as usize); - self.has_script_fn(Some(global), caches, lib, hash_script) + self.has_script_fn(global, caches, lib, hash_script) } .into(), false, @@ -639,7 +639,7 @@ impl Engine { }; let orig_source = mem::replace(&mut global.source, source.clone()); - let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); return if _is_method_call { // Method call of script function - map first argument to `this` @@ -668,7 +668,7 @@ impl Engine { backup.change_first_arg_to_copy(args); } - let args = &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| { + let args = &mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| { backup.restore_first_arg(a) }); @@ -694,7 +694,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -716,7 +716,7 @@ impl Engine { matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) }); #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) .map(|r| (r, arg_expr.start_position())) @@ -728,7 +728,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, fn_name: &str, mut hash: FnCallHashes, @@ -953,7 +953,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1087,7 +1087,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(None, &fn_name, num_params as usize); - self.has_script_fn(Some(global), caches, lib, hash_script) + self.has_script_fn(global, caches, lib, hash_script) } .into()); } @@ -1244,7 +1244,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1378,7 +1378,7 @@ impl Engine { let new_scope = &mut Scope::new(); let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); - let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, @@ -1426,7 +1426,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, script: &str, @@ -1471,7 +1471,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/func/native.rs b/src/func/native.rs index 63823ecd..65f5119d 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -8,7 +8,7 @@ use crate::tokenizer::{is_valid_function_name, Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult, - RhaiResultOf, StaticVec, VarDefInfo, ERR, + RhaiResultOf, SharedModule, StaticVec, VarDefInfo, ERR, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -74,7 +74,7 @@ pub struct NativeCallContext<'a> { /// The current [`GlobalRuntimeState`], if any. global: Option<&'a GlobalRuntimeState>, /// The current stack of loaded [modules][Module]. - lib: &'a [Shared], + lib: &'a [SharedModule], /// [Position] of the function call. pos: Position, /// The current nesting level of function calls. @@ -93,7 +93,7 @@ pub struct NativeCallContextStore { /// The current [`GlobalRuntimeState`], if any. pub global: GlobalRuntimeState, /// The current stack of loaded [modules][Module]. - pub lib: StaticVec>, + pub lib: StaticVec, /// [Position] of the function call. pub pos: Position, /// The current nesting level of function calls. @@ -116,7 +116,7 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [Shared], + &'a [SharedModule], Position, usize, )> for NativeCallContext<'a> @@ -128,7 +128,7 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [Shared], + &'a [SharedModule], Position, usize, ), @@ -145,9 +145,9 @@ impl<'a> } } -impl<'a> From<(&'a Engine, &'a str, &'a [Shared])> for NativeCallContext<'a> { +impl<'a> From<(&'a Engine, &'a str, &'a [SharedModule])> for NativeCallContext<'a> { #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [Shared])) -> Self { + fn from(value: (&'a Engine, &'a str, &'a [SharedModule])) -> Self { Self { engine: value.0, fn_name: value.1, @@ -169,7 +169,7 @@ impl<'a> NativeCallContext<'a> { )] #[inline(always)] #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [Shared]) -> Self { + pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [SharedModule]) -> Self { Self { engine, fn_name, @@ -193,7 +193,7 @@ impl<'a> NativeCallContext<'a> { fn_name: &'a str, source: Option<&'a str>, global: &'a GlobalRuntimeState, - lib: &'a [Shared], + lib: &'a [SharedModule], pos: Position, level: usize, ) -> Self { @@ -291,7 +291,7 @@ impl<'a> NativeCallContext<'a> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator { self.global.iter().flat_map(|&g| g.iter_imports_raw()) } /// _(internals)_ The current [`GlobalRuntimeState`], if any. @@ -315,7 +315,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[Shared] { + pub const fn namespaces(&self) -> &[SharedModule] { self.lib } /// Call a function inside the call context with the provided arguments. diff --git a/src/func/script.rs b/src/func/script.rs index 06fa916a..f8553165 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -4,7 +4,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::eval::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, Shared, ERR}; +use crate::{Dynamic, Engine, Position, RhaiError, RhaiResult, Scope, SharedModule, ERR}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +26,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -228,9 +228,9 @@ impl Engine { #[must_use] pub(crate) fn has_script_fn( &self, - _global: Option<&GlobalRuntimeState>, + _global: &GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], hash_script: u64, ) -> bool { let cache = caches.fn_resolution_cache_mut(); @@ -247,7 +247,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] let result = result || // Then check imported modules - _global.map_or(false, |m| m.contains_qualified_fn(hash_script)) + _global.contains_qualified_fn(hash_script) // Then check sub-modules || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); diff --git a/src/lib.rs b/src/lib.rs index 12f36850..4fdb59f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,6 +240,9 @@ pub use func::Locked; use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash}; +/// A shared [`Module`]. +type SharedModule = Shared; + pub use rhai_codegen::*; pub use func::{plugin, FuncArgs}; diff --git a/src/module/mod.rs b/src/module/mod.rs index 6cb59198..0d78e0e4 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,7 +10,7 @@ use crate::func::{ use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection}; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext, - RhaiResultOf, Shared, SmartString, StaticVec, + RhaiResultOf, Shared, SharedModule, SmartString, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -172,7 +172,7 @@ pub struct Module { /// Custom types. custom_types: Option, /// Sub-modules. - modules: Option>>, + modules: Option>, /// [`Module`] variables. variables: Option>, /// Flattened collection of all [`Module`] variables, including those in sub-modules. @@ -754,7 +754,7 @@ impl Module { #[cfg(not(feature = "no_module"))] #[inline] #[must_use] - pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap> { + pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap { // We must assume that the user has changed the sub-modules // (otherwise why take a mutable reference?) self.all_functions = None; @@ -822,7 +822,7 @@ impl Module { pub fn set_sub_module( &mut self, name: impl Into, - sub_module: impl Into>, + sub_module: impl Into, ) -> &mut Self { self.modules .get_or_insert_with(|| Default::default()) @@ -1830,7 +1830,7 @@ impl Module { /// Get an iterator to the sub-modules in the [`Module`]. #[inline] - pub fn iter_sub_modules(&self) -> impl Iterator)> { + pub fn iter_sub_modules(&self) -> impl Iterator { self.modules .iter() .flat_map(|m| m.iter().map(|(k, m)| (k.as_str(), m))) diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 8a61d715..5310e6a8 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,5 +1,5 @@ use crate::{ - Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, StaticVec, ERR, + Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, StaticVec, ERR, STATIC_VEC_INLINE_SIZE, }; #[cfg(feature = "no_std")] @@ -138,7 +138,7 @@ impl ModuleResolver for ModuleResolversCollection { source_path: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { for resolver in &self.0 { match resolver.resolve(engine, source_path, path, pos) { Ok(module) => return Ok(module), diff --git a/src/module/resolvers/dummy.rs b/src/module/resolvers/dummy.rs index f5782ebc..4da90566 100644 --- a/src/module/resolvers/dummy.rs +++ b/src/module/resolvers/dummy.rs @@ -1,4 +1,4 @@ -use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR}; +use crate::{Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -45,7 +45,7 @@ impl ModuleResolver for DummyModuleResolver { _: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { Err(ERR::ErrorModuleNotFound(path.into(), pos).into()) } } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index d68ddfd1..ee56ac18 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -4,7 +4,8 @@ use crate::eval::GlobalRuntimeState; use crate::func::{locked_read, locked_write}; use crate::{ - Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR, + Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, + SharedModule, ERR, }; use std::{ @@ -51,7 +52,7 @@ pub struct FileModuleResolver { extension: Identifier, cache_enabled: bool, scope: Scope<'static>, - cache: Locked>>, + cache: Locked>, } impl Default for FileModuleResolver { @@ -258,7 +259,7 @@ impl FileModuleResolver { /// The next time this path is resolved, the script file will be loaded once again. #[inline] #[must_use] - pub fn clear_cache_for_path(&mut self, path: impl AsRef) -> Option> { + pub fn clear_cache_for_path(&mut self, path: impl AsRef) -> Option { locked_write(&self.cache) .remove_entry(path.as_ref()) .map(|(.., v)| v) @@ -293,7 +294,7 @@ impl FileModuleResolver { source: Option<&str>, path: &str, pos: Position, - ) -> Result, Box> { + ) -> Result> { // Load relative paths from source if there is no base path specified let source_path = global .as_ref() @@ -344,7 +345,7 @@ impl ModuleResolver for FileModuleResolver { global: &mut GlobalRuntimeState, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.impl_resolve(engine, Some(global), None, path, pos) } @@ -355,7 +356,7 @@ impl ModuleResolver for FileModuleResolver { source: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.impl_resolve(engine, None, source, path, pos) } diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index a7f2d5c4..6316a845 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -1,6 +1,6 @@ use crate::eval::GlobalRuntimeState; use crate::func::SendSync; -use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST}; +use crate::{Engine, Position, RhaiResultOf, SharedModule, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -25,7 +25,7 @@ pub trait ModuleResolver: SendSync { source: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf>; + ) -> RhaiResultOf; /// Resolve a module based on a path string, given a [`GlobalRuntimeState`]. /// @@ -38,7 +38,7 @@ pub trait ModuleResolver: SendSync { global: &mut GlobalRuntimeState, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.resolve(engine, global.source(), path, pos) } diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 8338a43a..bd2b2638 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -1,5 +1,6 @@ use crate::{ - Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, SmartString, ERR, + Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, SharedModule, SmartString, + ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -27,7 +28,7 @@ use std::{ /// engine.set_module_resolver(resolver); /// ``` #[derive(Debug, Clone, Default)] -pub struct StaticModuleResolver(BTreeMap>); +pub struct StaticModuleResolver(BTreeMap); impl StaticModuleResolver { /// Create a new [`StaticModuleResolver`]. @@ -65,7 +66,7 @@ impl StaticModuleResolver { } /// Remove a [module][Module] given its path. #[inline(always)] - pub fn remove(&mut self, path: &str) -> Option> { + pub fn remove(&mut self, path: &str) -> Option { self.0.remove(path) } /// Does the path exist? @@ -80,12 +81,12 @@ impl StaticModuleResolver { } /// Get an iterator of all the [modules][Module]. #[inline] - pub fn iter(&self) -> impl Iterator)> { + pub fn iter(&self) -> impl Iterator { self.0.iter().map(|(k, v)| (k.as_str(), v)) } /// Get a mutable iterator of all the [modules][Module]. #[inline] - pub fn iter_mut(&mut self) -> impl Iterator)> { + pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut().map(|(k, v)| (k.as_str(), v)) } /// Get an iterator of all the [module][Module] paths. @@ -95,7 +96,7 @@ impl StaticModuleResolver { } /// Get an iterator of all the [modules][Module]. #[inline(always)] - pub fn values(&self) -> impl Iterator> { + pub fn values(&self) -> impl Iterator { self.0.values() } /// Remove all [modules][Module]. @@ -130,8 +131,8 @@ impl StaticModuleResolver { } impl IntoIterator for StaticModuleResolver { - type Item = (Identifier, Shared); - type IntoIter = IntoIter>; + type Item = (Identifier, SharedModule); + type IntoIter = IntoIter; #[inline(always)] #[must_use] @@ -141,8 +142,8 @@ impl IntoIterator for StaticModuleResolver { } impl<'a> IntoIterator for &'a StaticModuleResolver { - type Item = (&'a Identifier, &'a Shared); - type IntoIter = Iter<'a, SmartString, Shared>; + type Item = (&'a Identifier, &'a SharedModule); + type IntoIter = Iter<'a, SmartString, SharedModule>; #[inline(always)] fn into_iter(self) -> Self::IntoIter { @@ -158,7 +159,7 @@ impl ModuleResolver for StaticModuleResolver { _: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.0 .get(path) .cloned() diff --git a/src/optimizer.rs b/src/optimizer.rs index aa204777..b9804024 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -61,7 +61,7 @@ struct OptimizerState<'a> { caches: Caches, /// [Module][crate::Module] containing script-defined functions. #[cfg(not(feature = "no_function"))] - lib: &'a [crate::Shared], + lib: &'a [crate::SharedModule], /// Optimization level. optimization_level: OptimizationLevel, } @@ -71,7 +71,7 @@ impl<'a> OptimizerState<'a> { #[inline(always)] pub fn new( engine: &'a Engine, - #[cfg(not(feature = "no_function"))] lib: &'a [crate::Shared], + #[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule], optimization_level: OptimizationLevel, ) -> Self { Self { @@ -1263,7 +1263,7 @@ fn optimize_top_level( statements: StmtBlockContainer, engine: &Engine, scope: &Scope, - #[cfg(not(feature = "no_function"))] lib: &[crate::Shared], + #[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule], optimization_level: OptimizationLevel, ) -> StmtBlockContainer { let mut statements = statements; diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 499a1762..dfa3d8d1 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,6 +1,6 @@ //! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages. -use crate::{Engine, Module, Shared}; +use crate::{Engine, Module, SharedModule}; pub(crate) mod arithmetic; pub(crate) mod array_basic; @@ -99,7 +99,7 @@ pub trait Package { /// Get a reference to a shared module from this package. #[must_use] - fn as_shared_module(&self) -> Shared; + fn as_shared_module(&self) -> SharedModule; } /// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module]) diff --git a/src/parser.rs b/src/parser.rs index ee107089..fd315bad 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,7 +8,7 @@ use crate::ast::{ SwitchCasesCollection, TryCatchBlock, }; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; -use crate::eval::GlobalRuntimeState; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, @@ -2906,15 +2906,17 @@ impl Engine { nesting_level: level, will_shadow, }; - let mut this_ptr = None; + let caches = &mut Caches::new(); + let this_ptr = &mut None; + let context = EvalContext::new( self, &mut state.global, - None, + caches, &[], level, &mut state.stack, - &mut this_ptr, + this_ptr, ); match filter(false, info, context) { diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index b7ac5468..85355d4b 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -4,7 +4,7 @@ use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult, - RhaiResultOf, StaticVec, AST, ERR, + RhaiResultOf, SharedModule, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -150,9 +150,9 @@ impl FnPtr { let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); - let lib: &[crate::Shared] = &[ + let lib: &[SharedModule] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { &[][..] diff --git a/src/types/restore.rs b/src/types/restore.rs index d2e6cd87..6d0ad595 100644 --- a/src/types/restore.rs +++ b/src/types/restore.rs @@ -12,18 +12,26 @@ pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> { } impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> { - /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope only when - /// `need_restore` is `true`. + /// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at + /// the end of scope only when `need_restore` is `true`. + /// + /// Beware that the end of scope means the end of its lifetime, not necessarily waiting until + /// the current block scope is exited. #[inline(always)] - pub fn new_if(need_restore: bool, value: &'a mut T, restore: R) -> Self { + pub fn lock_if(need_restore: bool, value: &'a mut T, restore: R) -> Self { Self { value, restore: if need_restore { Some(restore) } else { None }, } } - /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope. + + /// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at + /// the end of scope. + /// + /// Beware that the end of scope means the end of its lifetime, not necessarily waiting until + /// the current block scope is exited. #[inline(always)] - pub fn new(value: &'a mut T, restore: R) -> Self { + pub fn lock(value: &'a mut T, restore: R) -> Self { Self { value, restore: Some(restore), From 8f128f37f0dc6f702a81966e68fa049a32571207 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 16:16:42 +0800 Subject: [PATCH 112/116] Add Dynamic::NULL to simplify this pointer binding. --- src/api/call_fn.rs | 4 +++- src/api/eval.rs | 4 +++- src/api/run.rs | 3 ++- src/eval/chaining.rs | 9 +++++---- src/eval/debugger.rs | 8 ++++---- src/eval/eval_context.rs | 30 +++++++++++++++++++----------- src/eval/expr.rs | 24 +++++++++++++----------- src/eval/stmt.rs | 8 +++++--- src/func/call.rs | 33 ++++++++++++++------------------- src/func/script.rs | 2 +- src/optimizer.rs | 33 ++++++++++++++------------------- src/parser.rs | 4 ++-- src/serde/de.rs | 2 ++ src/serde/serialize.rs | 2 ++ src/types/dynamic.rs | 37 +++++++++++++++++++++++++++++++++++++ unreachable!() | 0 16 files changed, 126 insertions(+), 77 deletions(-) create mode 100644 unreachable!() diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index b467e54d..42f418e4 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -250,7 +250,9 @@ impl Engine { ) -> RhaiResult { let statements = ast.statements(); let lib = &[AsRef::::as_ref(ast).clone()]; - let mut this_ptr = this_ptr; + + let mut no_this_ptr = Dynamic::NULL; + let mut this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); let orig_scope_len = scope.len(); diff --git a/src/api/eval.rs b/src/api/eval.rs index 28bcdcbd..8770996d 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -198,8 +198,10 @@ impl Engine { #[cfg(not(feature = "no_function"))] AsRef::::as_ref(ast).clone(), ]; + let mut this = Dynamic::NULL; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; + + self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; } let typ = self.map_type_name(result.type_name()); diff --git a/src/api/run.rs b/src/api/run.rs index 90c6abf2..f1c25bb4 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -141,8 +141,9 @@ impl Engine { #[cfg(not(feature = "no_function"))] AsRef::::as_ref(ast).clone(), ]; + let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; + self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 021dfdc3..a37fd16e 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -45,7 +45,7 @@ impl Engine { caches: &mut Caches, lib: &[SharedModule], level: usize, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), _parent: &Expr, @@ -573,7 +573,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { @@ -630,9 +630,10 @@ impl Engine { let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); + let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, level, &mut None, obj_ptr, root, expr, options, rhs, + global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs, idx_values, chain_type, new_val, ) } @@ -664,7 +665,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, parent_options: ASTFlags, parent_chain_type: ChainType, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 8c2d94e5..124b3547 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -416,7 +416,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { @@ -443,7 +443,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { @@ -466,7 +466,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { let node = node.into(); @@ -513,7 +513,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: ASTNode<'a>, event: DebuggerEvent, ) -> Result, Box> { diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index dc50a63c..ed5d6193 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -8,7 +8,7 @@ use std::prelude::v1::*; /// Context of a script evaluation process. #[derive(Debug)] #[allow(dead_code)] -pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { +pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// The current [`Engine`]. engine: &'a Engine, /// The current [`Scope`]. @@ -20,12 +20,12 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current stack of imported [modules][Module]. lib: &'a [SharedModule], /// The current bound `this` pointer, if any. - this_ptr: &'t mut Option<&'pt mut Dynamic>, + this_ptr: &'t mut Dynamic, /// The current nesting level of function calls. level: usize, } -impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { +impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// Create a new [`EvalContext`]. #[inline(always)] #[must_use] @@ -36,7 +36,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { lib: &'a [SharedModule], level: usize, scope: &'s mut Scope<'ps>, - this_ptr: &'t mut Option<&'pt mut Dynamic>, + this_ptr: &'t mut Dynamic, ) -> Self { Self { engine, @@ -104,8 +104,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState { - &mut self.global + pub fn global_runtime_state_mut(&mut self) -> &mut GlobalRuntimeState { + self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline] @@ -121,16 +121,24 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { self.lib } /// The current bound `this` pointer, if any. - #[inline(always)] + #[inline] #[must_use] pub fn this_ptr(&self) -> Option<&Dynamic> { - self.this_ptr.as_ref().map(|v| &**v) + if self.this_ptr.is_null() { + None + } else { + Some(self.this_ptr) + } } /// Mutable reference to the current bound `this` pointer, if any. - #[inline(always)] + #[inline] #[must_use] - pub fn this_ptr_mut(&mut self) -> &mut Option<&'pt mut Dynamic> { - self.this_ptr + pub fn this_ptr_mut(&mut self) -> Option<&mut Dynamic> { + if self.this_ptr.is_null() { + None + } else { + Some(self.this_ptr) + } } /// The current nesting level of function calls. #[inline(always)] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 89f0d4a7..55467124 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -54,7 +54,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &'s mut Scope, - this_ptr: &'s mut Option<&mut Dynamic>, + this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { @@ -140,7 +140,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &'s mut Scope, - this_ptr: &'s mut Option<&mut Dynamic>, + this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { // Make sure that the pointer indirection is taken only when absolutely necessary. @@ -148,10 +148,11 @@ impl Engine { let (index, var_pos) = match expr { // Check if the variable is `this` Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => { - return this_ptr.as_mut().map_or_else( - || Err(ERR::ErrorUnboundThis(*pos).into()), - |val| Ok(((*val).into(), *pos)), - ) + return if this_ptr.is_null() { + Err(ERR::ErrorUnboundThis(*pos).into()) + } else { + Ok((this_ptr.into(), *pos)) + }; } _ if global.always_search_scope => (0, expr.start_position()), Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos), @@ -224,7 +225,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, ) -> RhaiResult { // Coded this way for better branch prediction. @@ -256,10 +257,11 @@ impl Engine { self.track_operation(global, expr.position())?; return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS { - this_ptr - .as_deref() - .cloned() - .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) + if this_ptr.is_null() { + ERR::ErrorUnboundThis(*var_pos).into() + } else { + Ok(this_ptr.clone()) + } } else { self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 1e232ddc..0f064711 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -29,7 +29,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, statements: &[Stmt], restore_orig_state: bool, ) -> RhaiResult { @@ -203,7 +203,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { @@ -912,8 +912,10 @@ impl Engine { scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { + let mut this = Dynamic::NULL; + self.eval_stmt_block( - global, caches, lib, level, scope, &mut None, statements, false, + global, caches, lib, level, scope, &mut this, statements, false, ) .or_else(|err| match *err { ERR::Return(out, ..) => Ok(out), diff --git a/src/func/call.rs b/src/func/call.rs index d7f0102d..8bc4cfe3 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,8 +11,8 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::types::RestoreOnDrop; use crate::{ - calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, SharedModule, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -407,6 +407,7 @@ impl Engine { }; if trigger { let scope = &mut &mut Scope::new(); + let mut this = Dynamic::NULL; let node = crate::ast::Stmt::Noop(pos); let node = (&node).into(); let event = match _result { @@ -415,7 +416,7 @@ impl Engine { }; if let Err(err) = self - .run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) + .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event) { _result = Err(err); } @@ -646,16 +647,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - global, - caches, - lib, - level, - scope, - &mut Some(*first_arg), - func, - rest_args, - true, - pos, + global, caches, lib, level, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function @@ -672,8 +664,10 @@ impl Engine { backup.restore_first_arg(a) }); + let mut this = Dynamic::NULL; + self.call_script_fn( - global, caches, lib, level, scope, &mut None, func, args, true, pos, + global, caches, lib, level, scope, &mut this, func, args, true, pos, ) } .map(|r| (r, false)); @@ -697,7 +691,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, arg_expr: &Expr, ) -> RhaiResultOf<(Dynamic, Position)> { // Literal values @@ -956,7 +950,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, fn_name: &str, op_token: Option<&Token>, first_arg: Option<&Expr>, @@ -1247,7 +1241,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, fn_name: &str, args_expr: &[Expr], @@ -1376,12 +1370,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 this = Dynamic::NULL; let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, + global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos, ) } @@ -1474,7 +1469,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &FnCallExpr, pos: Position, ) -> RhaiResult { diff --git a/src/func/script.rs b/src/func/script.rs index f8553165..ae4a0f77 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -29,7 +29,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, args: &mut FnCallArgs, rewind_scope: bool, diff --git a/src/optimizer.rs b/src/optimizer.rs index b9804024..7ad765c1 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -50,7 +50,7 @@ struct OptimizerState<'a> { /// Has the [`AST`] been changed during this pass? changed: bool, /// Collection of constants to use for eager function evaluations. - variables: StaticVec<(Identifier, AccessMode, Option)>, + variables: StaticVec<(Identifier, AccessMode, Dynamic)>, /// Activate constants propagation? propagate_constants: bool, /// An [`Engine`] instance for eager function evaluation. @@ -108,12 +108,7 @@ impl<'a> OptimizerState<'a> { } /// Add a new variable to the list. #[inline(always)] - pub fn push_var( - &mut self, - name: impl Into, - access: AccessMode, - value: Option, - ) { + pub fn push_var(&mut self, name: impl Into, access: AccessMode, value: Dynamic) { self.variables.push((name.into(), access, value)); } /// Look up a constant from the list. @@ -127,7 +122,8 @@ impl<'a> OptimizerState<'a> { if n == name { return match access { AccessMode::ReadWrite => None, - AccessMode::ReadOnly => value.as_ref(), + AccessMode::ReadOnly if value.is_null() => None, + AccessMode::ReadOnly => Some(value), }; } } @@ -141,7 +137,7 @@ impl<'a> OptimizerState<'a> { fn_name: &str, op_token: Option<&Token>, arg_values: &mut [Dynamic], - ) -> Option { + ) -> Dynamic { #[cfg(not(feature = "no_function"))] let lib = self.lib; #[cfg(feature = "no_function")] @@ -160,8 +156,7 @@ impl<'a> OptimizerState<'a> { false, Position::NONE, ) - .ok() - .map(|(v, ..)| v) + .map_or(Dynamic::NULL, |(v, ..)| v) } } @@ -271,13 +266,13 @@ fn optimize_stmt_block( state.push_var( x.0.as_str(), AccessMode::ReadOnly, - x.1.get_literal_value(), + x.1.get_literal_value().unwrap_or(Dynamic::NULL), ); } } else { // Add variables into the state optimize_expr(&mut x.1, state, false); - state.push_var(x.0.as_str(), AccessMode::ReadWrite, None); + state.push_var(x.0.as_str(), AccessMode::ReadWrite, Dynamic::NULL); } } // Optimize the statement @@ -1197,13 +1192,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::>>().unwrap(); let result = match x.name.as_str() { - KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), + KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(), #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), + crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE, _ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values) }; - if let Some(result) = result { + if !result.is_null() { state.set_dirty(); *expr = Expr::from_dynamic(result, *pos); return; @@ -1289,15 +1284,15 @@ fn optimize_top_level( .rev() .flat_map(|m| m.iter_var()) { - state.push_var(name, AccessMode::ReadOnly, Some(value.clone())); + state.push_var(name, AccessMode::ReadOnly, value.clone()); } // Add constants and variables from the scope for (name, constant, value) in scope.iter() { if constant { - state.push_var(name, AccessMode::ReadOnly, Some(value)); + state.push_var(name, AccessMode::ReadOnly, value); } else { - state.push_var(name, AccessMode::ReadWrite, None); + state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL); } } diff --git a/src/parser.rs b/src/parser.rs index fd315bad..03d5b16b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2907,7 +2907,7 @@ impl Engine { will_shadow, }; let caches = &mut Caches::new(); - let this_ptr = &mut None; + let mut this = Dynamic::NULL; let context = EvalContext::new( self, @@ -2916,7 +2916,7 @@ impl Engine { &[], level, &mut state.stack, - this_ptr, + &mut this, ); match filter(false, info, context) { diff --git a/src/serde/de.rs b/src/serde/de.rs index fcb26a9d..42d4739f 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -125,6 +125,8 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_any>(self, visitor: V) -> RhaiResultOf { match self.0 .0 { + Union::Null => unreachable!(), + Union::Unit(..) => self.deserialize_unit(visitor), Union::Bool(..) => self.deserialize_bool(visitor), Union::Str(..) => self.deserialize_str(visitor), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 424de0d5..0e9c481f 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -15,6 +15,8 @@ use crate::types::dynamic::Variant; impl Serialize for Dynamic { fn serialize(&self, ser: S) -> Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => ser.serialize_unit(), Union::Bool(x, ..) => ser.serialize_bool(x), Union::Str(ref s, ..) => ser.serialize_str(s.as_str()), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index c53b8ea3..eeee966a 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -55,6 +55,9 @@ pub struct Dynamic(pub(crate) Union); /// /// Most variants are boxed to reduce the size. pub enum Union { + /// An error value which should not exist. + Null, + /// The Unit value - (). Unit((), Tag, AccessMode), /// A boolean value. @@ -178,6 +181,8 @@ impl Dynamic { #[must_use] pub const fn tag(&self) -> Tag { match self.0 { + Union::Null => unreachable!(), + Union::Unit(_, tag, _) | Union::Bool(_, tag, _) | Union::Str(_, tag, _) @@ -203,6 +208,8 @@ impl Dynamic { /// Attach arbitrary data to this [`Dynamic`]. pub fn set_tag(&mut self, value: Tag) -> &mut Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(_, ref mut tag, _) | Union::Bool(_, ref mut tag, _) | Union::Str(_, ref mut tag, _) @@ -226,6 +233,12 @@ impl Dynamic { } self } + /// Is this [`Dynamic`] null? + #[inline(always)] + #[must_use] + pub(crate) const fn is_null(&self) -> bool { + matches!(self.0, Union::Null) + } /// Does this [`Dynamic`] hold a variant data type instead of one of the supported system /// primitive types? #[inline(always)] @@ -307,6 +320,8 @@ impl Dynamic { #[must_use] pub fn type_id(&self) -> TypeId { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => TypeId::of::<()>(), Union::Bool(..) => TypeId::of::(), Union::Str(..) => TypeId::of::(), @@ -341,6 +356,8 @@ impl Dynamic { #[must_use] pub fn type_name(&self) -> &'static str { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => "()", Union::Bool(..) => "bool", Union::Str(..) => "string", @@ -385,6 +402,8 @@ impl Hash for Dynamic { mem::discriminant(&self.0).hash(state); match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => (), Union::Bool(ref b, ..) => b.hash(state), Union::Str(ref s, ..) => s.hash(state), @@ -416,6 +435,8 @@ impl Hash for Dynamic { impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => Ok(()), Union::Bool(ref v, ..) => fmt::Display::fmt(v, f), Union::Str(ref v, ..) => fmt::Display::fmt(v, f), @@ -509,6 +530,8 @@ impl fmt::Debug for Dynamic { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f), Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f), Union::Str(ref v, ..) => fmt::Debug::fmt(v, f), @@ -619,6 +642,8 @@ impl Clone for Dynamic { /// The cloned copy is marked read-write even if the original is read-only. fn clone(&self) -> Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)), Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)), Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)), @@ -666,6 +691,9 @@ use std::f32::consts as FloatConstants; use std::f64::consts as FloatConstants; impl Dynamic { + /// A [`Dynamic`] containing a `null`. + pub(crate) const NULL: Self = Self(Union::Null); + /// A [`Dynamic`] containing a `()`. pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing a `true`. @@ -888,6 +916,8 @@ impl Dynamic { #[must_use] pub(crate) const fn access_mode(&self) -> AccessMode { match self.0 { + Union::Null => unreachable!(), + Union::Unit(.., access) | Union::Bool(.., access) | Union::Str(.., access) @@ -913,6 +943,8 @@ impl Dynamic { /// Set the [`AccessMode`] for this [`Dynamic`]. pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(.., ref mut access) | Union::Bool(.., ref mut access) | Union::Str(.., ref mut access) @@ -1107,6 +1139,7 @@ impl Dynamic { let _access = self.access_mode(); match self.0 { + Union::Null => unreachable!(), Union::Shared(..) => self, _ => Self(Union::Shared( crate::Locked::new(self).into(), @@ -1151,6 +1184,8 @@ impl Dynamic { reify!(self, |v: T| return Some(v)); match self.0 { + Union::Null => unreachable!(), + Union::Int(v, ..) => reify!(v => Option), #[cfg(not(feature = "no_float"))] Union::Float(v, ..) => reify!(*v => Option), @@ -1485,6 +1520,7 @@ impl Dynamic { } match self.0 { + Union::Null => unreachable!(), Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, @@ -1583,6 +1619,7 @@ impl Dynamic { } match self.0 { + Union::Null => unreachable!(), Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, diff --git a/unreachable!() b/unreachable!() new file mode 100644 index 00000000..e69de29b From 5bae4d8a19f396038b84fcbcc0741db9e30c45a3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 16:37:35 +0800 Subject: [PATCH 113/116] Fix builds. --- src/api/register.rs | 2 +- src/ast/ast.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index 9e2f7490..a335faa1 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -662,7 +662,7 @@ impl Engine { /// let mut module = Module::new(); /// module.set_native_fn("calc", |x: i64| Ok(x + 1)); /// - /// let module: SharedModule = module.into(); + /// let module: Shared = module.into(); /// /// engine /// // Register the module as a fixed sub-module diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 76277e7b..c1cfbaed 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -267,7 +267,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub(crate) const fn shared_lib(&self) -> &SharedModule { + pub(crate) const fn shared_lib(&self) -> &crate::SharedModule { &self.lib } /// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions. From e93923b3b6a41c47554d2a3759aa5519c0ccc0bd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 21:28:20 +0800 Subject: [PATCH 114/116] Move level into GlobalRuntimeState. --- CHANGELOG.md | 11 +- src/api/call_fn.rs | 5 +- src/api/deprecated.rs | 22 +++- src/api/eval.rs | 12 +- src/api/run.rs | 4 +- src/eval/chaining.rs | 185 ++++++++++++--------------- src/eval/debugger.rs | 18 ++- src/eval/eval_context.rs | 8 +- src/eval/expr.rs | 68 +++++----- src/eval/global_state.rs | 3 + src/eval/stmt.rs | 254 ++++++++++++++++++-------------------- src/func/call.rs | 109 ++++++++-------- src/func/native.rs | 74 ++--------- src/func/script.rs | 12 +- src/module/mod.rs | 2 +- src/optimizer.rs | 3 +- src/packages/debugging.rs | 90 ++++++-------- src/parser.rs | 5 +- src/tests.rs | 4 +- src/types/fn_ptr.rs | 13 +- unreachable!() | 0 21 files changed, 409 insertions(+), 493 deletions(-) delete mode 100644 unreachable!() diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a606c13..4f785f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.11.0 ============== +Speed Improvements +------------------ + +* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. + Bug fixes --------- @@ -12,10 +17,10 @@ Bug fixes * Functions marked `global` in `import`ed modules with no alias names now work properly. * Incorrect loop optimizations that are too aggressive (e.g. unrolling a `do { ... } until true` with a `break` statement inside) and cause crashes are removed. -Speed Improvements ------------------- +Breaking changes +---------------- -* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. +* `NativeCallContext::new` is completely deprecated and unimplemented (always panics) in favor of new API's. New features ------------ diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 42f418e4..6ba9fcf3 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -267,7 +267,7 @@ impl Engine { }); let result = if eval_ast && !statements.is_empty() { - let r = self.eval_global_statements(global, caches, lib, 0, scope, statements); + let r = self.eval_global_statements(global, caches, lib, scope, statements); if rewind_scope { scope.rewind(orig_scope_len); @@ -289,7 +289,6 @@ impl Engine { global, caches, lib, - 0, scope, &mut this_ptr, fn_def, @@ -306,7 +305,7 @@ impl Engine { if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this_ptr, node)?; + self.run_debugger(global, caches, lib, scope, &mut this_ptr, node)?; } Ok(result) diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 6b6ad977..cda495db 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -4,7 +4,7 @@ use crate::func::RegisterNativeFunction; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext, - Position, RhaiResult, RhaiResultOf, Scope, AST, + Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -354,6 +354,26 @@ impl Dynamic { } impl NativeCallContext<'_> { + /// Create a new [`NativeCallContext`]. + /// + /// # Unimplemented + /// + /// This method is deprecated. It is no longer implemented and always panics. + /// + /// Use [`FnPtr::call`] to call a function pointer directly. + /// + /// This method will be removed in the next major version. + #[deprecated( + since = "1.3.0", + note = "use `FnPtr::call` to call a function pointer directly." + )] + #[inline(always)] + #[must_use] + #[allow(unused_variables)] + pub fn new(engine: &Engine, fn_name: &str, lib: &[SharedModule]) -> Self { + unimplemented!("`NativeCallContext::new` is deprecated"); + } + /// Call a function inside the call context. /// /// # Deprecated diff --git a/src/api/eval.rs b/src/api/eval.rs index 8770996d..829c69b0 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -189,7 +189,7 @@ impl Engine { let global = &mut GlobalRuntimeState::new(self); let caches = &mut Caches::new(); - let result = self.eval_ast_with_scope_raw(global, caches, 0, scope, ast)?; + let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?; #[cfg(feature = "debugging")] if self.debugger.is_some() { @@ -201,7 +201,7 @@ impl Engine { let mut this = Dynamic::NULL; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; + self.run_debugger(global, caches, lib, scope, &mut this, node)?; } let typ = self.map_type_name(result.type_name()); @@ -217,7 +217,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - level: usize, + scope: &mut Scope, ast: &'a AST, ) -> RhaiResult { @@ -244,7 +244,7 @@ impl Engine { AsRef::::as_ref(ast).clone(), ]; - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } /// _(internals)_ Evaluate a list of statements with no `this` pointer. /// Exported under the `internals` feature only. @@ -261,11 +261,11 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[crate::SharedModule], - level: usize, + scope: &mut Scope, statements: &[crate::ast::Stmt], ) -> RhaiResult { - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } } diff --git a/src/api/run.rs b/src/api/run.rs index f1c25bb4..551b5e98 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -131,7 +131,7 @@ impl Engine { } else { &lib }; - self.eval_global_statements(global, caches, lib, 0, scope, statements)?; + self.eval_global_statements(global, caches, lib, scope, statements)?; } #[cfg(feature = "debugging")] @@ -143,7 +143,7 @@ impl Engine { ]; let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; + self.run_debugger(global, caches, lib, scope, &mut this, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index a37fd16e..ca813d86 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -44,7 +44,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), @@ -76,7 +75,7 @@ impl Engine { if !parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); let mut idx_val_for_setter = idx_val.clone(); @@ -85,13 +84,13 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - global, caches, lib, level, target, idx_val, idx_pos, false, true, + global, caches, lib, target, idx_val, idx_pos, false, true, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, obj_ptr, root, rhs, *options, + global, caches, lib, this_ptr, obj_ptr, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) { Ok((result, true)) if is_obj_temp_val => { @@ -107,7 +106,7 @@ impl Engine { let idx = &mut idx_val_for_setter; let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx, new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, is_ref_mut, ) .or_else(|e| match *e { ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), @@ -120,19 +119,19 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let (new_val, op_info) = new_val.take().expect("`Some`"); let idx_val = &mut idx_values.pop().unwrap(); let idx = &mut idx_val.clone(); - let try_setter = match self.get_indexed_mut( - global, caches, lib, level, target, idx, pos, true, false, - ) { + let try_setter = match self + .get_indexed_mut(global, caches, lib, target, idx, pos, true, false) + { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - global, caches, lib, level, op_info, obj_ptr, root, new_val, + global, caches, lib, op_info, obj_ptr, root, new_val, )?; self.check_data_size(obj_ptr, op_info.pos)?; None @@ -151,13 +150,12 @@ impl Engine { // Call the index getter to get the current value if let Ok(val) = - self.call_indexer_get(global, caches, lib, level, target, idx) + self.call_indexer_get(global, caches, lib, target, idx) { let mut val = val.into(); // Run the op-assignment self.eval_op_assignment( - global, caches, lib, level, op_info, &mut val, root, - new_val, + global, caches, lib, op_info, &mut val, root, new_val, )?; // Replace new value new_val = val.take_or_clone(); @@ -169,7 +167,7 @@ impl Engine { let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx_val, new_val, is_ref_mut, + global, caches, lib, target, idx_val, new_val, is_ref_mut, )?; } @@ -178,14 +176,12 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); - self.get_indexed_mut( - global, caches, lib, level, target, idx_val, pos, false, true, - ) - .map(|v| (v.take_or_clone(), false)) + self.get_indexed_mut(global, caches, lib, target, idx_val, pos, false, true) + .map(|v| (v.take_or_clone(), false)) } } } @@ -201,9 +197,8 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, rhs, - )?; + let reset = self + .run_debugger_with_reset(global, caches, lib, scope, this_ptr, rhs)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -222,8 +217,7 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, pos1, - *pos, + global, caches, lib, name, *hashes, target, call_args, pos1, *pos, ) } // xxx.fn_name(...) = ??? @@ -237,16 +231,16 @@ impl Engine { // {xxx:map}.id op= ??? Expr::Property(x, pos) if target.is::() && new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let (new_val, op_info) = new_val.take().expect("`Some`"); { let val_target = &mut self.get_indexed_mut( - global, caches, lib, level, target, index, *pos, true, false, + global, caches, lib, target, index, *pos, true, false, )?; self.eval_op_assignment( - global, caches, lib, level, op_info, val_target, root, new_val, + global, caches, lib, op_info, val_target, root, new_val, )?; } self.check_data_size(target.source(), op_info.pos)?; @@ -255,18 +249,18 @@ impl Engine { // {xxx:map}.id Expr::Property(x, pos) if target.is::() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let val = self.get_indexed_mut( - global, caches, lib, level, target, index, *pos, false, false, + global, caches, lib, target, index, *pos, false, false, )?; Ok((val.take_or_clone(), false)) } // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let ((getter, hash_get), (setter, hash_set), name) = &**x; let (mut new_val, op_info) = new_val.take().expect("`Some`"); @@ -275,15 +269,15 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, - is_ref_mut, *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, + *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, level, target, &mut prop, + global, caches, lib, target, &mut prop, ) .map(|r| (r, false)) .map_err(|e| { @@ -300,7 +294,7 @@ impl Engine { let orig_val = &mut (&mut orig_val).into(); self.eval_op_assignment( - global, caches, lib, level, op_info, orig_val, root, new_val, + global, caches, lib, op_info, orig_val, root, new_val, )?; } @@ -309,8 +303,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, lib, level, setter, None, *hash_set, args, is_ref_mut, - *pos, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -318,7 +311,7 @@ impl Engine { let idx = &mut name.into(); let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx, new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, is_ref_mut, ) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -331,27 +324,24 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, is_ref_mut, - *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos, ) .map_or_else( |err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); - self.call_indexer_get( - global, caches, lib, level, target, &mut prop, - ) - .map(|r| (r, false)) - .map_err(|e| match *e { - ERR::ErrorIndexingType(..) => err, - _ => e, - }) + self.call_indexer_get(global, caches, lib, target, &mut prop) + .map(|r| (r, false)) + .map_err(|e| match *e { + ERR::ErrorIndexingType(..) => err, + _ => e, + }) } _ => Err(err), }, @@ -368,20 +358,18 @@ impl Engine { let val_target = &mut match x.lhs { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger( - global, caches, lib, level, scope, this_ptr, _node, - )?; + self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; let index = &mut p.2.clone().into(); self.get_indexed_mut( - global, caches, lib, level, target, index, pos, false, true, + global, caches, lib, target, index, pos, false, true, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, + global, caches, lib, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -402,8 +390,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, - pos1, pos, + global, caches, lib, name, *hashes, target, call_args, pos1, + pos, )? .0 .into() @@ -418,8 +406,8 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val_target, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, new_val, + global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs, + idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } @@ -431,9 +419,7 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger( - global, caches, lib, level, scope, this_ptr, _node, - )?; + self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; let ((getter, hash_get), (setter, hash_set), name) = &**p; let rhs_chain = rhs.into(); @@ -443,7 +429,7 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, pos, ) .or_else(|err| match *err { @@ -451,7 +437,7 @@ impl Engine { ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, level, target, &mut prop, + global, caches, lib, target, &mut prop, ) .map(|r| (r, false)) .map_err( @@ -468,8 +454,8 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val, root, rhs, - *options, &x.rhs, idx_values, rhs_chain, new_val, + global, caches, lib, this_ptr, val, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -479,7 +465,7 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_native_fn_call( - global, caches, lib, level, setter, None, *hash_set, args, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, pos, ) .or_else( @@ -489,8 +475,8 @@ impl Engine { let idx = &mut name.into(); let new_val = val; self.call_indexer_set( - global, caches, lib, level, target, idx, - new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, + is_ref_mut, ) .or_else(|e| match *e { // If there is no setter, no need to feed it @@ -513,7 +499,7 @@ impl Engine { let val = { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, + global, caches, lib, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -535,8 +521,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, - call_args, pos1, pos, + global, caches, lib, name, *hashes, target, call_args, + pos1, pos, )? .0 }; @@ -545,7 +531,7 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val, root, rhs, *options, + global, caches, lib, this_ptr, val, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(pos)) @@ -571,7 +557,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -612,8 +597,7 @@ impl Engine { // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( - global, caches, lib, level, scope, this_ptr, rhs, options, chain_type, - idx_values, + global, caches, lib, scope, this_ptr, rhs, options, chain_type, idx_values, )?; } } @@ -622,19 +606,19 @@ impl Engine { // id.??? or id[???] Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, lhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, lhs)?; self.track_operation(global, *var_pos)?; let (mut target, ..) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs, - idx_values, chain_type, new_val, + global, caches, lib, &mut this, obj_ptr, root, expr, options, rhs, idx_values, + chain_type, new_val, ) } // {expr}.??? = ??? or {expr}[???] = ??? @@ -642,14 +626,14 @@ impl Engine { // {expr}.??? or {expr}[???] expr => { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, obj_ptr, root, expr, options, rhs, - idx_values, chain_type, new_val, + global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, + chain_type, new_val, ) } } @@ -663,7 +647,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -680,7 +663,7 @@ impl Engine { { for arg_expr in &x.args { idx_values.push( - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg_expr)? + self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? .0 .flatten(), ); @@ -714,11 +697,9 @@ impl Engine { { for arg_expr in &x.args { _arg_values.push( - self.get_arg_value( - global, caches, lib, level, scope, this_ptr, arg_expr, - )? - .0 - .flatten(), + self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? + .0 + .flatten(), ); } } @@ -733,7 +714,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => { _arg_values.push( - self.eval_expr(global, caches, lib, level, scope, this_ptr, lhs)? + self.eval_expr(global, caches, lib, scope, this_ptr, lhs)? .flatten(), ); } @@ -744,8 +725,7 @@ impl Engine { let chain_type = expr.into(); self.eval_dot_index_chain_arguments( - global, caches, lib, level, scope, this_ptr, rhs, *options, chain_type, - idx_values, + global, caches, lib, scope, this_ptr, rhs, *options, chain_type, idx_values, )?; if !_arg_values.is_empty() { @@ -759,7 +739,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + self.eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(), ), _ => unreachable!("unknown chained expression: {:?}", expr), @@ -775,7 +755,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &mut Dynamic, idx: &mut Dynamic, ) -> RhaiResultOf { @@ -783,12 +762,12 @@ impl Engine { let hash = global.hash_idx_get(); let fn_name = crate::engine::FN_IDX_GET; let pos = Position::NONE; - let level = level + 1; - self.exec_native_fn_call( - global, caches, lib, level, fn_name, None, hash, args, true, pos, - ) - .map(|(r, ..)| r) + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + + self.exec_native_fn_call(global, caches, lib, fn_name, None, hash, args, true, pos) + .map(|(r, ..)| r) } /// Call a set indexer. @@ -798,7 +777,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &mut Dynamic, idx: &mut Dynamic, new_val: &mut Dynamic, @@ -808,10 +786,12 @@ impl Engine { let args = &mut [target, idx, new_val]; let fn_name = crate::engine::FN_IDX_SET; let pos = Position::NONE; - let level = level + 1; + + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); self.exec_native_fn_call( - global, caches, lib, level, fn_name, None, hash, args, is_ref_mut, pos, + global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos, ) } @@ -822,7 +802,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, idx_pos: Position, @@ -1031,7 +1010,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, caches, lib, level, target, idx) + .call_indexer_get(global, caches, lib, target, idx) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 124b3547..6eed7279 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -414,14 +414,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { if let Some(cmd) = - self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node)? + self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node)? { global.debugger.status = cmd; } @@ -441,13 +440,12 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { - self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node) + self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node) } else { Ok(None) } @@ -464,7 +462,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, @@ -497,7 +494,7 @@ impl Engine { }, }; - self.run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) + self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) } /// Run the debugger callback unconditionally. /// @@ -511,7 +508,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: ASTNode<'a>, @@ -519,7 +515,7 @@ impl Engine { ) -> Result, Box> { let src = global.source_raw().cloned(); let src = src.as_ref().map(|s| s.as_str()); - let context = crate::EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = crate::EvalContext::new(self, global, caches, lib, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; @@ -545,12 +541,12 @@ impl Engine { // Bump a level if it is a function call let level = match node { ASTNode::Expr(Expr::FnCall(..)) | ASTNode::Stmt(Stmt::FnCall(..)) => { - level + 1 + global.level + 1 } ASTNode::Stmt(Stmt::Expr(e)) if matches!(**e, Expr::FnCall(..)) => { - level + 1 + global.level + 1 } - _ => level, + _ => global.level, }; global.debugger.status = DebuggerStatus::FunctionExit(level); Ok(None) diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index ed5d6193..f8f2fd1a 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -21,8 +21,6 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { lib: &'a [SharedModule], /// The current bound `this` pointer, if any. this_ptr: &'t mut Dynamic, - /// The current nesting level of function calls. - level: usize, } impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { @@ -34,7 +32,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { global: &'g mut GlobalRuntimeState, caches: &'c mut Caches, lib: &'a [SharedModule], - level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Dynamic, ) -> Self { @@ -45,7 +42,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { caches, lib, this_ptr, - level, } } /// The current [`Engine`]. @@ -144,7 +140,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { #[inline(always)] #[must_use] pub const fn call_level(&self) -> usize { - self.level + self.global.level } /// Evaluate an [expression tree][crate::Expression] within this [evaluation context][`EvalContext`]. @@ -186,7 +182,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global, self.caches, self.lib, - self.level, self.scope, self.this_ptr, statements, @@ -196,7 +191,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global, self.caches, self.lib, - self.level, self.scope, self.this_ptr, expr, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 55467124..0c18772a 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -52,25 +52,23 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(_, Some(_), _) => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + self.search_scope_only(global, caches, lib, scope, this_ptr, expr) } Expr::Variable(v, None, _var_pos) => match &**v { // Normal variable access #[cfg(not(feature = "no_module"))] (_, ns, ..) if ns.is_empty() => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + self.search_scope_only(global, caches, lib, scope, this_ptr, expr) } #[cfg(feature = "no_module")] - (_, (), ..) => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) - } + (_, (), ..) => self.search_scope_only(global, caches, lib, scope, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] @@ -138,7 +136,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, @@ -174,7 +172,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -223,7 +221,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -235,8 +233,7 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -244,7 +241,7 @@ impl Engine { self.track_operation(global, expr.position())?; - return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); } // Then variable access. @@ -252,7 +249,7 @@ impl Engine { // will cost more than the mis-predicted `match` branch. if let Expr::Variable(x, index, var_pos) = expr { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; @@ -263,14 +260,13 @@ impl Engine { Ok(this_ptr.clone()) } } else { - self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) + self.search_namespace(global, caches, lib, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) }; } #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -300,14 +296,11 @@ impl Engine { let result = x .iter() .try_for_each(|expr| { - let item = - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let item = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; op_info.pos = expr.start_position(); - self.eval_op_assignment( - global, caches, lib, level, &op_info, target, root, item, - ) + self.eval_op_assignment(global, caches, lib, &op_info, target, root, item) }) .map(|_| concat.take_or_clone()); @@ -324,7 +317,7 @@ impl Engine { crate::Array::with_capacity(x.len()), |mut array, item_expr| { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, item_expr)? + .eval_expr(global, caches, lib, scope, this_ptr, item_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -356,7 +349,7 @@ impl Engine { x.0.iter() .try_fold(x.1.clone(), |mut map, (key, value_expr)| { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, value_expr)? + .eval_expr(global, caches, lib, scope, this_ptr, value_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -379,30 +372,30 @@ impl Engine { } Expr::And(x, ..) => Ok((self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? && self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Or(x, ..) => Ok((self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? || self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Coalesce(x, ..) => { - let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)?; + let value = self.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?; if value.is::<()>() { - self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) + self.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs) } else { Ok(value) } @@ -422,8 +415,7 @@ impl Engine { *pos, )) })?; - let mut context = - EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let mut context = EvalContext::new(self, global, caches, lib, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); @@ -431,17 +423,17 @@ impl Engine { } Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), - Expr::Stmt(x) => { - self.eval_stmt_block(global, caches, lib, level, scope, this_ptr, x, true) - } + Expr::Stmt(x) => self.eval_stmt_block(global, caches, lib, scope, this_ptr, x, true), #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), + Expr::Index(..) => { + self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + } #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), + Expr::Dot(..) => { + self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + } _ => unreachable!("expression cannot be evaluated: {:?}", expr), } diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 413dee13..94dcb837 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -38,6 +38,8 @@ pub struct GlobalRuntimeState { /// Number of modules loaded. #[cfg(not(feature = "no_module"))] pub num_modules_loaded: usize, + /// The current nesting level of function calls. + pub level: usize, /// Level of the current scope. /// /// The global (root) level is zero, a new block (or function call) is one level higher, and so on. @@ -87,6 +89,7 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] num_modules_loaded: 0, scope_level: 0, + level: 0, always_search_scope: false, #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 0f064711..faea3923 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -27,7 +27,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, statements: &[Stmt], @@ -54,6 +53,7 @@ impl Engine { let global = &mut *RestoreOnDrop::lock_if(restore_orig_state, global, move |g| { g.scope_level -= 1; + #[cfg(not(feature = "no_module"))] g.truncate_imports(orig_imports_len); @@ -77,7 +77,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, stmt, @@ -119,7 +118,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, op_info: &OpAssignment, target: &mut Target, root: (&str, Position), @@ -143,14 +141,17 @@ impl Engine { let hash = *hash_op_assign; let args = &mut [&mut *lock_guard, &mut new_val]; - let level = level + 1; if self.fast_operators() { if let Some(func) = get_builtin_op_assignment_fn(op_assign_token, args[0], args[1]) { // Built-in found let op = op_assign_token.literal_syntax(); - let context = (self, op, None, &*global, lib, *op_pos, level).into(); + + global.level += 1; + let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let context = (self, op, None, global, lib, *op_pos).into(); return func(context, args).map(|_| ()); } } @@ -160,7 +161,7 @@ impl Engine { let token = Some(op_assign_token); match self.exec_native_fn_call( - global, caches, lib, level, op_assign, token, hash, args, true, *op_pos, + global, caches, lib, op_assign, token, hash, args, true, *op_pos, ) { Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => @@ -170,7 +171,7 @@ impl Engine { *args[0] = self .exec_native_fn_call( - global, caches, lib, level, op, token, *hash_op, args, true, *op_pos, + global, caches, lib, op, token, *hash_op, args, true, *op_pos, ) .map_err(|err| err.fill_position(op_info.pos))? .0 @@ -201,15 +202,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, stmt)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); @@ -220,7 +219,7 @@ impl Engine { if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; - return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); } // Then assignments. @@ -233,11 +232,11 @@ impl Engine { if let Expr::Variable(x, ..) = lhs { let rhs_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, rhs)? .flatten(); let (mut lhs_ptr, pos) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; let var_name = x.3.as_str(); @@ -259,13 +258,13 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; return self - .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) + .eval_op_assignment(global, caches, lib, op_info, lhs_ptr, root, rhs_val) .map(|_| Dynamic::UNIT); } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] { - let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; + let rhs_val = self.eval_expr(global, caches, lib, scope, this_ptr, rhs)?; // Check if the result is a string. If so, intern it. #[cfg(not(feature = "no_closure"))] @@ -292,14 +291,12 @@ impl Engine { } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self.eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ), + Expr::Index(..) => self + .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self.eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ), + Expr::Dot(..) => self + .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), _ => unreachable!("cannot assign to expression: {:?}", lhs), } .map(|_| Dynamic::UNIT); @@ -314,32 +311,28 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .map(Dynamic::flatten), // Block scope Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT), - Stmt::Block(statements, ..) => self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ), + Stmt::Block(statements, ..) => { + self.eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + } // If statement Stmt::If(x, ..) => { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if guard_val && !if_block.is_empty() { - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, if_block, true, - ) + self.eval_stmt_block(global, caches, lib, scope, this_ptr, if_block, true) } else if !guard_val && !else_block.is_empty() { - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, else_block, true, - ) + self.eval_stmt_block(global, caches, lib, scope, this_ptr, else_block, true) } else { Ok(Dynamic::UNIT) } @@ -359,7 +352,7 @@ impl Engine { let mut result = None; - let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let value = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; if value.is_hashable() { let hasher = &mut get_hasher(); @@ -376,7 +369,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .eval_expr(global, caches, lib, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -398,7 +391,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .eval_expr(global, caches, lib, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -416,7 +409,7 @@ impl Engine { result .or_else(|| def_case.as_ref().map(|&index| &expressions[index].expr)) .map_or(Ok(Dynamic::UNIT), |expr| { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) + self.eval_expr(global, caches, lib, scope, this_ptr, expr) }) } @@ -431,8 +424,8 @@ impl Engine { } loop { - if let Err(err) = self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -449,7 +442,7 @@ impl Engine { loop { let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -461,8 +454,8 @@ impl Engine { continue; } - if let Err(err) = self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -480,9 +473,9 @@ impl Engine { loop { if !body.is_empty() { - if let Err(err) = self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) + { match *err { ERR::LoopBreak(false, ..) => continue, ERR::LoopBreak(true, value, ..) => break Ok(value), @@ -492,7 +485,7 @@ impl Engine { } let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -507,7 +500,7 @@ impl Engine { let (var_name, counter, expr, statements) = &**x; let iter_obj = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let iter_type = iter_obj.type_id(); @@ -531,75 +524,77 @@ impl Engine { .find_map(|m| m.get_qualified_iter(iter_type)) }); - if let Some(func) = func { - // Restore scope at end of statement - let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::lock(scope, move |s| { - s.rewind(orig_scope_len); - }); + let func = func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?; - // Add the loop variables - let counter_index = if counter.is_empty() { - usize::MAX - } else { - scope.push(counter.name.clone(), 0 as INT); - scope.len() - 1 - }; + // Restore scope at end of statement + let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::lock(scope, move |s| { + s.rewind(orig_scope_len); + }); - scope.push(var_name.name.clone(), ()); - let index = scope.len() - 1; - - func(iter_obj) - .enumerate() - .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - return Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => return Err(err.fill_position(expr.position())), - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - self.track_operation(global, statements.position())?; - - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ) - .map(|_| Dynamic::UNIT) - .or_else(|err| match *err { - ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), - _ => Err(err), - }) - }) - .or_else(|err| match *err { - ERR::LoopBreak(true, value, ..) => Ok(value), - _ => Err(err), - }) + // Add the loop variables + let counter_index = if counter.is_empty() { + usize::MAX } else { - Err(ERR::ErrorFor(expr.start_position()).into()) + scope.push(counter.name.clone(), 0 as INT); + scope.len() - 1 + }; + + scope.push(var_name.name.clone(), ()); + let index = scope.len() - 1; + + let mut result = Dynamic::UNIT; + + for (x, iter_value) in func(iter_obj).enumerate() { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; + + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); + } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + // Set loop value + let value = iter_value + .map_err(|err| err.fill_position(expr.position()))? + .flatten(); + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + // Run block + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + continue; + } + + match self + .eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => { + result = value; + break; + } + _ => return Err(err), + }, + } } + + Ok(result) } // Continue/Break statement @@ -607,7 +602,7 @@ impl Engine { let is_break = options.contains(ASTFlags::BREAK); let value = if let Some(ref expr) = expr { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + self.eval_expr(global, caches, lib, scope, this_ptr, expr)? } else { Dynamic::UNIT }; @@ -626,9 +621,7 @@ impl Engine { catch_block, } = &**x; - match self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) - { + match self.eval_stmt_block(global, caches, lib, scope, this_ptr, try_block, true) { r @ Ok(_) => r, Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), @@ -683,7 +676,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, catch_block, @@ -704,7 +696,7 @@ impl Engine { // Throw value Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), // Empty throw @@ -714,7 +706,7 @@ impl Engine { // Return value Stmt::Return(Some(expr), .., pos) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), // Empty return @@ -746,8 +738,7 @@ impl Engine { nesting_level, will_shadow, }; - let context = - EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); if !filter(true, info, context)? { return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); @@ -756,7 +747,7 @@ impl Engine { // Evaluate initial value let mut value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let _alias = if !rewind_scope { @@ -811,7 +802,7 @@ impl Engine { return Err(ERR::ErrorTooManyModules(*_pos).into()); } - let v = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let v = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; let typ = v.type_name(); let path = v.try_cast::().ok_or_else(|| { self.make_type_mismatch_err::(typ, expr.position()) @@ -908,21 +899,18 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { let mut this = Dynamic::NULL; - self.eval_stmt_block( - global, caches, lib, level, scope, &mut this, statements, false, - ) - .or_else(|err| match *err { - ERR::Return(out, ..) => Ok(out), - ERR::LoopBreak(..) => { - unreachable!("no outer loop scope to break out of") - } - _ => Err(err), - }) + self.eval_stmt_block(global, caches, lib, scope, &mut this, statements, false) + .or_else(|err| match *err { + ERR::Return(out, ..) => Ok(out), + ERR::LoopBreak(..) => { + unreachable!("no outer loop scope to break out of") + } + _ => Err(err), + }) } } diff --git a/src/func/call.rs b/src/func/call.rs index 8bc4cfe3..38020404 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -326,7 +326,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, name: &str, op_token: Option<&Token>, hash: u64, @@ -385,7 +384,7 @@ impl Engine { // Run external function let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, lib, pos, level).into(); + let context = (self, name, src, &*global, lib, pos).into(); let mut _result = if func.is_plugin_fn() { let f = func.get_plugin_fn().unwrap(); @@ -401,7 +400,7 @@ impl Engine { #[cfg(feature = "debugging")] { let trigger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, + crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::Next(.., true) => true, _ => false, }; @@ -415,8 +414,8 @@ impl Engine { Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - if let Err(err) = self - .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event) + if let Err(err) = + self.run_debugger_raw(global, caches, lib, scope, &mut this, node, event) { _result = Err(err); } @@ -539,7 +538,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, _scope: Option<&mut Scope>, fn_name: &str, op_token: Option<&Token>, @@ -561,7 +559,8 @@ impl Engine { #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, args, is_ref_mut)?; - let level = level + 1; + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); // These may be redirected from method style calls. if hashes.is_native_only() { @@ -647,7 +646,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - global, caches, lib, level, scope, first_arg, func, rest_args, true, pos, + global, caches, lib, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function @@ -667,7 +666,7 @@ impl Engine { let mut this = Dynamic::NULL; self.call_script_fn( - global, caches, lib, level, scope, &mut this, func, args, true, pos, + global, caches, lib, scope, &mut this, func, args, true, pos, ) } .map(|r| (r, false)); @@ -678,7 +677,7 @@ impl Engine { let hash = hashes.native(); self.exec_native_fn_call( - global, caches, lib, level, fn_name, op_token, hash, args, is_ref_mut, pos, + global, caches, lib, fn_name, op_token, hash, args, is_ref_mut, pos, ) } @@ -689,7 +688,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, arg_expr: &Expr, @@ -699,7 +697,7 @@ impl Engine { self.track_operation(global, arg_expr.start_position())?; #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, arg_expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, arg_expr)?; return Ok((value, arg_expr.start_position())); } @@ -712,7 +710,7 @@ impl Engine { #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); - self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) + self.eval_expr(global, caches, lib, scope, this_ptr, arg_expr) .map(|r| (r, arg_expr.start_position())) } @@ -723,7 +721,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, @@ -764,7 +761,6 @@ impl Engine { global, caches, lib, - level, None, fn_name, None, @@ -820,7 +816,6 @@ impl Engine { global, caches, lib, - level, None, &fn_name, None, @@ -921,7 +916,6 @@ impl Engine { global, caches, lib, - level, None, fn_name, None, @@ -948,7 +942,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, fn_name: &str, @@ -974,7 +967,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1015,7 +1008,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; // Fn - only in function call style return arg_value @@ -1030,7 +1023,7 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1042,7 +1035,7 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { let (value, ..) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, expr)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1055,7 +1048,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; return Ok(arg_value.is_shared().into()); } @@ -1064,14 +1057,14 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, &a_expr[0])?; + self.get_arg_value(global, caches, lib, scope, this_ptr, &a_expr[0])?; let num_params = arg_value .as_int() @@ -1090,7 +1083,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1105,12 +1098,15 @@ impl Engine { let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; let s = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; - let result = - self.eval_script_expr_in_place(global, caches, lib, level + 1, scope, s, pos); + + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let result = self.eval_script_expr_in_place(global, caches, lib, scope, s, pos); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1152,7 +1148,7 @@ impl Engine { .copied() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1163,8 +1159,8 @@ impl Engine { return self .exec_fn_call( - global, caches, lib, level, scope, name, op_token, hashes, &mut args, - is_ref_mut, false, pos, + global, caches, lib, scope, name, op_token, hashes, &mut args, is_ref_mut, + false, pos, ) .map(|(v, ..)| v); } @@ -1180,16 +1176,16 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, first_expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, first_expr)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, first_expr)?; + self.search_namespace(global, caches, lib, scope, this_ptr, first_expr)?; if target.is_read_only() { target = target.into_owned(); @@ -1216,7 +1212,7 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1226,8 +1222,7 @@ impl Engine { } self.exec_fn_call( - global, caches, lib, level, None, name, op_token, hashes, &mut args, is_ref_mut, false, - pos, + global, caches, lib, None, name, op_token, hashes, &mut args, is_ref_mut, false, pos, ) .map(|(v, ..)| v) } @@ -1239,7 +1234,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, @@ -1260,20 +1254,20 @@ impl Engine { // and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, &args_expr[0])?; + self.run_debugger(global, caches, lib, scope, this_ptr, &args_expr[0])?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; // Get target reference to first argument let first_arg = &args_expr[0]; let (target, _pos) = - self.search_scope_only(global, caches, lib, level, scope, this_ptr, first_arg)?; + self.search_scope_only(global, caches, lib, scope, this_ptr, first_arg)?; self.track_operation(global, _pos)?; @@ -1296,7 +1290,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1363,7 +1357,8 @@ impl Engine { } } - let level = level + 1; + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); match func { #[cfg(not(feature = "no_function"))] @@ -1376,12 +1371,12 @@ impl Engine { let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos, + global, caches, lib, new_scope, &mut this, fn_def, &mut args, true, pos, ) } Some(f) if f.is_plugin_fn() => { - let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); let f = f.get_plugin_fn().expect("plugin function"); let result = if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into()) @@ -1393,7 +1388,7 @@ impl Engine { Some(f) if f.is_native() => { let func = f.get_native_fn().expect("native function"); - let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); let result = func(context, &mut args); self.check_return_value(result, pos) } @@ -1422,7 +1417,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, script: &str, _pos: Position, @@ -1458,7 +1452,7 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } /// Evaluate a function call expression. @@ -1467,7 +1461,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &FnCallExpr, @@ -1489,12 +1482,12 @@ impl Engine { // Short-circuit native binary operator call if under Fast Operators mode if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self - .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[0])? + .get_arg_value(global, caches, lib, scope, this_ptr, &args[0])? .0 .flatten(); let mut rhs = self - .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[1])? + .get_arg_value(global, caches, lib, scope, this_ptr, &args[1])? .0 .flatten(); @@ -1504,14 +1497,16 @@ impl Engine { get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1]) { // Built-in found - let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); + global.level += 1; + let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let context = (self, name.as_str(), None, global, lib, pos).into(); return func(context, operands); } return self .exec_fn_call( - global, caches, lib, level, None, name, op_token, *hashes, operands, false, - false, pos, + global, caches, lib, None, name, op_token, *hashes, operands, false, false, pos, ) .map(|(v, ..)| v); } @@ -1522,7 +1517,7 @@ impl Engine { let hash = hashes.native(); return self.make_qualified_function_call( - global, caches, lib, level, scope, this_ptr, namespace, name, args, hash, pos, + global, caches, lib, scope, this_ptr, namespace, name, args, hash, pos, ); } @@ -1533,7 +1528,7 @@ impl Engine { ); self.make_function_call( - global, caches, lib, level, scope, this_ptr, name, op_token, first_arg, args, *hashes, + global, caches, lib, scope, this_ptr, name, op_token, first_arg, args, *hashes, *capture, pos, ) } diff --git a/src/func/native.rs b/src/func/native.rs index 65f5119d..de74f6c6 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -72,13 +72,11 @@ pub struct NativeCallContext<'a> { /// Function source, if any. source: Option<&'a str>, /// The current [`GlobalRuntimeState`], if any. - global: Option<&'a GlobalRuntimeState>, + global: &'a GlobalRuntimeState, /// The current stack of loaded [modules][Module]. lib: &'a [SharedModule], /// [Position] of the function call. pos: Position, - /// The current nesting level of function calls. - level: usize, } /// _(internals)_ Context of a native Rust function call. @@ -96,8 +94,6 @@ pub struct NativeCallContextStore { pub lib: StaticVec, /// [Position] of the function call. pub pos: Position, - /// The current nesting level of function calls. - pub level: usize, } #[cfg(feature = "internals")] @@ -118,7 +114,6 @@ impl<'a> &'a GlobalRuntimeState, &'a [SharedModule], Position, - usize, )> for NativeCallContext<'a> { #[inline(always)] @@ -130,56 +125,20 @@ impl<'a> &'a GlobalRuntimeState, &'a [SharedModule], Position, - usize, ), ) -> Self { Self { engine: value.0, fn_name: value.1, source: value.2, - global: Some(value.3), + global: value.3, lib: value.4, pos: value.5, - level: value.6, - } - } -} - -impl<'a> From<(&'a Engine, &'a str, &'a [SharedModule])> for NativeCallContext<'a> { - #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [SharedModule])) -> Self { - Self { - engine: value.0, - fn_name: value.1, - source: None, - global: None, - lib: value.2, - pos: Position::NONE, - level: 0, } } } impl<'a> NativeCallContext<'a> { - /// _(internals)_ Create a new [`NativeCallContext`]. - /// Exported under the `internals` feature only. - #[deprecated( - since = "1.3.0", - note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly." - )] - #[inline(always)] - #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [SharedModule]) -> Self { - Self { - engine, - fn_name, - source: None, - global: None, - lib, - pos: Position::NONE, - level: 0, - } - } /// _(internals)_ Create a new [`NativeCallContext`]. /// Exported under the `internals` feature only. /// @@ -195,16 +154,14 @@ impl<'a> NativeCallContext<'a> { global: &'a GlobalRuntimeState, lib: &'a [SharedModule], pos: Position, - level: usize, ) -> Self { Self { engine, fn_name, source, - global: Some(global), + global, lib, pos, - level, } } @@ -218,10 +175,9 @@ impl<'a> NativeCallContext<'a> { engine, fn_name: &context.fn_name, source: context.source.as_ref().map(String::as_str), - global: Some(&context.global), + global: &context.global, lib: &context.lib, pos: context.pos, - level: context.level, } } /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`]. @@ -233,10 +189,9 @@ impl<'a> NativeCallContext<'a> { NativeCallContextStore { fn_name: self.fn_name.to_string(), source: self.source.map(|s| s.to_string()), - global: self.global.unwrap().clone(), + global: self.global.clone(), lib: self.lib.iter().cloned().collect(), pos: self.pos, - level: self.level, } } @@ -262,7 +217,7 @@ impl<'a> NativeCallContext<'a> { #[inline(always)] #[must_use] pub const fn call_level(&self) -> usize { - self.level + self.global.level } /// The current source. #[inline(always)] @@ -274,7 +229,7 @@ impl<'a> NativeCallContext<'a> { #[inline(always)] #[must_use] pub fn tag(&self) -> Option<&Dynamic> { - self.global.as_ref().map(|g| &g.tag) + Some(&self.global.tag) } /// Get an iterator over the current set of modules imported via `import` statements /// in reverse order. @@ -283,7 +238,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.global.iter().flat_map(|&g| g.iter_imports()) + self.global.iter_imports() } /// Get an iterator over the current set of modules imported via `import` statements in reverse order. #[cfg(not(feature = "no_module"))] @@ -292,7 +247,7 @@ impl<'a> NativeCallContext<'a> { pub(crate) fn iter_imports_raw( &self, ) -> impl Iterator { - self.global.iter().flat_map(|&g| g.iter_imports_raw()) + self.global.iter_imports_raw() } /// _(internals)_ The current [`GlobalRuntimeState`], if any. /// Exported under the `internals` feature only. @@ -301,7 +256,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> { + pub const fn global_runtime_state(&self) -> &GlobalRuntimeState { self.global } /// Get an iterator over the namespaces containing definitions of all script-defined functions @@ -437,10 +392,7 @@ impl<'a> NativeCallContext<'a> { is_method_call: bool, args: &mut [&mut Dynamic], ) -> RhaiResult { - let global = &mut self - .global - .cloned() - .unwrap_or_else(|| GlobalRuntimeState::new(self.engine())); + let mut global = &mut self.global.clone(); let caches = &mut Caches::new(); let fn_name = fn_name.as_ref(); @@ -448,6 +400,8 @@ impl<'a> NativeCallContext<'a> { let op_token = op_token.as_ref(); let args_len = args.len(); + global.level += 1; + if native_only { return self .engine() @@ -455,7 +409,6 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, - self.level + 1, fn_name, op_token, calc_fn_hash(None, fn_name, args_len), @@ -483,7 +436,6 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, - self.level + 1, None, fn_name, op_token, diff --git a/src/func/script.rs b/src/func/script.rs index ae4a0f77..db3fa542 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -27,7 +27,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, @@ -66,7 +65,7 @@ impl Engine { self.track_operation(global, pos)?; // Check for stack overflow - if level > self.max_call_levels() { + if global.level > self.max_call_levels() { return Err(ERR::ErrorStackOverflow(pos).into()); } @@ -140,7 +139,7 @@ impl Engine { #[cfg(feature = "debugging")] { let node = crate::ast::Stmt::Noop(fn_def.body.position()); - self.run_debugger(global, caches, lib, level, scope, this_ptr, &node)?; + self.run_debugger(global, caches, lib, scope, this_ptr, &node)?; } // Evaluate the function @@ -149,7 +148,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, &fn_def.body, @@ -179,7 +177,7 @@ impl Engine { #[cfg(feature = "debugging")] { let trigger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, + crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::Next(.., true) => true, _ => false, }; @@ -190,9 +188,7 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self - .run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) - { + match self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) { Ok(_) => (), Err(err) => _result = Err(err), } diff --git a/src/module/mod.rs b/src/module/mod.rs index 0d78e0e4..a8676655 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1986,7 +1986,7 @@ impl Module { // Run the script let caches = &mut crate::eval::Caches::new(); - let result = engine.eval_ast_with_scope_raw(global, caches, 0, &mut scope, ast); + let result = engine.eval_ast_with_scope_raw(global, caches, &mut scope, ast); // Create new module let mut module = Module::new(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 7ad765c1..9bf57005 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -148,7 +148,6 @@ impl<'a> OptimizerState<'a> { &mut self.global, &mut self.caches, lib, - 0, fn_name, op_token, calc_fn_hash(None, fn_name, arg_values.len()), @@ -1144,7 +1143,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(feature = "no_function")] let lib = &[][..]; - let context = (state.engine, x.name.as_str(), lib).into(); + let context = (state.engine, x.name.as_str(),None, &state.global, lib, *pos).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs index 8a800c1b..4e3cbc35 100644 --- a/src/packages/debugging.rs +++ b/src/packages/debugging.rs @@ -33,57 +33,47 @@ mod debugging_functions { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_index"))] pub fn back_trace(ctx: NativeCallContext) -> Array { - if let Some(global) = ctx.global_runtime_state() { - global - .debugger - .call_stack() - .iter() - .rev() - .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { - fn_name.as_str() != "back_trace" || !args.is_empty() - }) - .map( - |frame @ crate::debugger::CallStackFrame { - fn_name: _fn_name, - args: _args, - source: _source, - pos: _pos, - }| { - let display = frame.to_string(); + ctx.global_runtime_state() + .debugger + .call_stack() + .iter() + .rev() + .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { + fn_name.as_str() != "back_trace" || !args.is_empty() + }) + .map( + |frame @ crate::debugger::CallStackFrame { + fn_name: _fn_name, + args: _args, + source: _source, + pos: _pos, + }| { + let display = frame.to_string(); - #[cfg(not(feature = "no_object"))] - { - let mut map = Map::new(); - map.insert("display".into(), display.into()); - map.insert("fn_name".into(), _fn_name.into()); - if !_args.is_empty() { - map.insert( - "args".into(), - Dynamic::from_array(_args.clone().to_vec()), - ); - } - if let Some(source) = _source { - map.insert("source".into(), source.into()); - } - if !_pos.is_none() { - map.insert( - "line".into(), - (_pos.line().unwrap() as crate::INT).into(), - ); - map.insert( - "position".into(), - (_pos.position().unwrap_or(0) as crate::INT).into(), - ); - } - Dynamic::from_map(map) + #[cfg(not(feature = "no_object"))] + { + let mut map = Map::new(); + map.insert("display".into(), display.into()); + map.insert("fn_name".into(), _fn_name.into()); + if !_args.is_empty() { + map.insert("args".into(), Dynamic::from_array(_args.clone().to_vec())); } - #[cfg(feature = "no_object")] - display.into() - }, - ) - .collect() - } else { - Array::new() - } + if let Some(source) = _source { + map.insert("source".into(), source.into()); + } + if !_pos.is_none() { + map.insert("line".into(), (_pos.line().unwrap() as crate::INT).into()); + map.insert( + "position".into(), + (_pos.position().unwrap_or(0) as crate::INT).into(), + ); + } + Dynamic::from_map(map) + } + #[cfg(feature = "no_object")] + display.into() + }, + ) + .collect() } } diff --git a/src/parser.rs b/src/parser.rs index 03d5b16b..fed55ac3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2898,12 +2898,12 @@ impl Engine { if let Some(ref filter) = self.def_var_filter { let will_shadow = state.stack.iter().any(|(v, ..)| v == name); - let level = settings.level; + state.global.level = settings.level; let is_const = access == AccessMode::ReadOnly; let info = VarDefInfo { name: &name, is_const, - nesting_level: level, + nesting_level: state.global.level, will_shadow, }; let caches = &mut Caches::new(); @@ -2914,7 +2914,6 @@ impl Engine { &mut state.global, caches, &[], - level, &mut state.stack, &mut this, ); diff --git a/src/tests.rs b/src/tests.rs index 8bbef0d4..6e36c977 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -45,9 +45,9 @@ fn check_struct_sizes() { assert_eq!( size_of::(), if cfg!(feature = "no_position") { - 72 + 64 } else { - 80 + 72 } ); } diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 85355d4b..3348d6c2 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -1,5 +1,6 @@ //! The `FnPtr` type. +use crate::eval::GlobalRuntimeState; use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ @@ -160,8 +161,16 @@ impl FnPtr { &lib }; - #[allow(deprecated)] - let ctx = NativeCallContext::new(engine, self.fn_name(), lib); + let global = &mut GlobalRuntimeState::new(engine); + + let ctx = NativeCallContext::new_with_all_fields( + engine, + self.fn_name(), + None, + global, + lib, + Position::NONE, + ); let result = self.call_raw(&ctx, None, arg_values)?; diff --git a/unreachable!() b/unreachable!() deleted file mode 100644 index e69de29b..00000000 From ae1e19c98a1123795cc4cbdc5ea3c61d6fa10fd4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 21:41:00 +0800 Subject: [PATCH 115/116] Fix builds. --- src/api/call_fn.rs | 15 +++++++-------- src/api/eval.rs | 3 +-- src/eval/stmt.rs | 3 +-- src/types/fn_ptr.rs | 11 ++--------- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 6ba9fcf3..1a78cae1 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -3,7 +3,6 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; -use crate::types::RestoreOnDrop; use crate::{ reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, StaticVec, AST, ERR, @@ -252,7 +251,7 @@ impl Engine { let lib = &[AsRef::::as_ref(ast).clone()]; let mut no_this_ptr = Dynamic::NULL; - let mut this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); + let this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); let orig_scope_len = scope.len(); @@ -262,7 +261,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::lock(global, move |g| { + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); @@ -278,11 +277,11 @@ impl Engine { Ok(Dynamic::UNIT) } .and_then(|_| { - let mut args: StaticVec<_> = arg_values.iter_mut().collect(); + let args = &mut arg_values.iter_mut().collect::>(); // Check for data race. #[cfg(not(feature = "no_closure"))] - crate::func::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?; + crate::func::ensure_no_data_race(name, args, false).map(|_| Dynamic::UNIT)?; if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) { self.call_script_fn( @@ -290,9 +289,9 @@ impl Engine { caches, lib, scope, - &mut this_ptr, + this_ptr, fn_def, - &mut args, + args, rewind_scope, Position::NONE, ) @@ -305,7 +304,7 @@ impl Engine { if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, scope, &mut this_ptr, node)?; + self.run_debugger(global, caches, lib, scope, this_ptr, node)?; } Ok(result) diff --git a/src/api/eval.rs b/src/api/eval.rs index 829c69b0..10cf9040 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,7 +3,6 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; use crate::types::dynamic::Variant; -use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR, }; @@ -229,7 +228,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::lock(global, move |g| { + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index faea3923..9a086ed9 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -730,12 +730,11 @@ impl Engine { // Check variable definition filter if let Some(ref filter) = self.def_var_filter { let will_shadow = scope.contains(var_name); - let nesting_level = global.scope_level; let is_const = access == AccessMode::ReadOnly; let info = VarDefInfo { name: var_name, is_const, - nesting_level, + nesting_level: global.scope_level, will_shadow, }; let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 3348d6c2..5d087ef2 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -161,16 +161,9 @@ impl FnPtr { &lib }; - let global = &mut GlobalRuntimeState::new(engine); + let global = &GlobalRuntimeState::new(engine); - let ctx = NativeCallContext::new_with_all_fields( - engine, - self.fn_name(), - None, - global, - lib, - Position::NONE, - ); + let ctx = (engine, self.fn_name(), None, global, lib, Position::NONE).into(); let result = self.call_raw(&ctx, None, arg_values)?; From abe625c336b063888685359c97435270379a1213 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Wed, 9 Nov 2022 20:14:36 +0900 Subject: [PATCH 116/116] fix CI --- examples/definitions/.rhai/all_in_one.d.rhai | 291 ++++++------------ .../.rhai/definitions/__static__.d.rhai | 291 ++++++------------ examples/definitions/.rhai/defs.json | 8 +- src/config/hashing.rs | 12 +- 4 files changed, 189 insertions(+), 413 deletions(-) diff --git a/examples/definitions/.rhai/all_in_one.d.rhai b/examples/definitions/.rhai/all_in_one.d.rhai index c1025aa8..4ec27f7f 100644 --- a/examples/definitions/.rhai/all_in_one.d.rhai +++ b/examples/definitions/.rhai/all_in_one.d.rhai @@ -677,8 +677,6 @@ op **(u64, int) -> u64; op **(u8, int) -> u8; -op +(Decimal) -> Decimal; - op +(int) -> int; op +(f32) -> f32; @@ -801,8 +799,6 @@ op +=(Instant, float) -> (); /// Add the specified number of `seconds` to the timestamp. op +=(Instant, int) -> (); -op -(Decimal) -> Decimal; - op -(int) -> int; op -(f32) -> f32; @@ -1131,9 +1127,6 @@ op ^(u64, u64) -> u64; op ^(u8, u8) -> u8; -/// Return the absolute value of the decimal number. -fn abs(x: Decimal) -> Decimal; - /// Return the absolute value of the number. fn abs(x: int) -> int; @@ -1306,13 +1299,6 @@ fn atan(x: float, y: float) -> float; /// Return the arc-hyperbolic-tangent of the floating-point number, in radians. fn atanh(x: float) -> float; -/// Get an array of object maps containing the function calls stack. -/// -/// If there is no debugging interface registered, an empty array is returned. -/// -/// An array of strings is returned under `no_object`. -fn back_trace() -> Array; - /// Return an iterator over all the bits in the number. /// /// # Example @@ -1426,9 +1412,6 @@ fn blob(len: int, value: int) -> Blob; /// ``` fn bytes(string: String) -> int; -/// Return the smallest whole number larger than or equals to the decimal number. -fn ceiling(x: Decimal) -> Decimal; - /// Return the smallest whole number larger than or equals to the floating-point number. fn ceiling(x: float) -> float; @@ -1570,8 +1553,63 @@ fn clear(string: String) -> (); /// ``` fn contains(array: Array, value: ?) -> bool; -/// Return the cosine of the decimal number in radians. -fn cos(x: Decimal) -> Decimal; +/// Return `true` if the BLOB contains a specified byte value. +/// +/// # Example +/// +/// ```rhai +/// let text = "hello, world!"; +/// +/// print(text.contains('h')); // prints true +/// +/// print(text.contains('x')); // prints false +/// ``` +fn contains(blob: Blob, value: int) -> bool; + +/// Returns `true` if the object map contains a specified property. +/// +/// # Example +/// +/// ```rhai +/// let m = #{a: 1, b: 2, c: 3}; +/// +/// print(m.contains("b")); // prints true +/// +/// print(m.contains("x")); // prints false +/// ``` +fn contains(map: Map, property: String) -> bool; + +/// Return `true` if the range contains a specified value. +fn contains(range: ExclusiveRange, value: int) -> bool; + +/// Return `true` if the range contains a specified value. +fn contains(range: InclusiveRange, value: int) -> bool; + +/// Return `true` if the string contains a specified character. +/// +/// # Example +/// +/// ```rhai +/// let text = "hello, world!"; +/// +/// print(text.contains('h')); // prints true +/// +/// print(text.contains('x')); // prints false +/// ``` +fn contains(string: String, character: char) -> bool; + +/// Return `true` if the string contains a specified string. +/// +/// # Example +/// +/// ```rhai +/// let text = "hello, world!"; +/// +/// print(text.contains("hello")); // prints true +/// +/// print(text.contains("hey")); // prints false +/// ``` +fn contains(string: String, match_string: String) -> bool; /// Return the cosine of the floating-point number in radians. fn cos(x: float) -> float; @@ -1650,37 +1688,37 @@ fn crop(string: String, start: int) -> (); fn crop(string: String, start: int, len: int) -> (); /// Return the empty string. -fn debug() -> String; +op debug() -> String; /// Convert the array into a string. -fn debug(array: Array) -> String; +op debug(Array) -> String; /// Convert the string into debug format. -fn debug(character: char) -> String; +op debug(char) -> String; /// Convert the function pointer into a string in debug format. -fn debug(f: FnPtr) -> String; +op debug(FnPtr) -> String; /// Convert the value of the `item` into a string in debug format. -fn debug(item: ?) -> String; +op debug(?) -> String; /// Convert the object map into a string. -fn debug(map: Map) -> String; +op debug(Map) -> String; /// Convert the value of `number` into a string. -fn debug(number: f32) -> String; +op debug(f32) -> String; /// Convert the value of `number` into a string. -fn debug(number: float) -> String; +op debug(float) -> String; /// Convert the string into debug format. -fn debug(string: String) -> String; +op debug(String) -> String; /// Convert the unit into a string in debug format. -fn debug(unit: ()) -> String; +op debug(()) -> String; /// Convert the boolean value into a string in debug format. -fn debug(value: bool) -> String; +op debug(bool) -> String; /// Remove duplicated _consecutive_ elements from the array. /// @@ -1986,9 +2024,6 @@ fn end(range: InclusiveRange) -> int; /// ``` fn ends_with(string: String, match_string: String) -> bool; -/// Return the exponential of the decimal number. -fn exp(x: Decimal) -> Decimal; - /// Return the exponential of the floating-point number. fn exp(x: float) -> float; @@ -2199,15 +2234,9 @@ fn filter(array: Array, filter: FnPtr) -> Array; /// ``` fn filter(array: Array, filter_func: String) -> Array; -/// Return the largest whole number less than or equals to the decimal number. -fn floor(x: Decimal) -> Decimal; - /// Return the largest whole number less than or equals to the floating-point number. fn floor(x: float) -> float; -/// Return the fractional part of the decimal number. -fn fraction(x: Decimal) -> Decimal; - /// Return the fractional part of the floating-point number. fn fraction(x: float) -> float; @@ -2309,9 +2338,6 @@ fn get bits(value: int) -> Iterator; /// ``` fn get bytes(string: String) -> int; -/// Return the smallest whole number larger than or equals to the decimal number. -fn get ceiling(x: Decimal) -> Decimal; - /// Return the smallest whole number larger than or equals to the floating-point number. fn get ceiling(x: float) -> float; @@ -2345,21 +2371,12 @@ fn get end(range: ExclusiveRange) -> int; /// Return the end of the inclusive range. fn get end(range: InclusiveRange) -> int; -/// Return the largest whole number less than or equals to the decimal number. -fn get floor(x: Decimal) -> Decimal; - /// Return the largest whole number less than or equals to the floating-point number. fn get floor(x: float) -> float; -/// Return the fractional part of the decimal number. -fn get fraction(x: Decimal) -> Decimal; - /// Return the fractional part of the floating-point number. fn get fraction(x: float) -> float; -/// Return the integral part of the decimal number. -fn get int(x: Decimal) -> Decimal; - /// Return the integral part of the floating-point number. fn get int(x: float) -> float; @@ -2470,9 +2487,6 @@ fn get is_odd(x: u64) -> bool; /// Return true if the number is odd. fn get is_odd(x: u8) -> bool; -/// Return true if the decimal number is zero. -fn get is_zero(x: Decimal) -> bool; - /// Return true if the number is zero. fn get is_zero(x: int) -> bool; @@ -2549,10 +2563,6 @@ fn get len(string: String) -> int; /// ``` fn get name(fn_ptr: FnPtr) -> String; -/// Return the nearest whole number closest to the decimal number. -/// Always round mid-point towards the closest even number. -fn get round(x: Decimal) -> Decimal; - /// Return the nearest whole number closest to the floating-point number. /// Rounds away from zero. fn get round(x: float) -> float; @@ -2917,9 +2927,6 @@ fn insert(array: Array, index: int, item: ?) -> (); /// ``` fn insert(blob: Blob, index: int, value: int) -> (); -/// Return the integral part of the decimal number. -fn int(x: Decimal) -> Decimal; - /// Return the integral part of the floating-point number. fn int(x: float) -> float; @@ -3033,9 +3040,6 @@ fn is_odd(x: u64) -> bool; /// Return true if the number is odd. fn is_odd(x: u8) -> bool; -/// Return true if the decimal number is zero. -fn is_zero(x: Decimal) -> bool; - /// Return true if the number is zero. fn is_zero(x: int) -> bool; @@ -3113,15 +3117,9 @@ fn len(map: Map) -> int; /// ``` fn len(string: String) -> int; -/// Return the natural log of the decimal number. -fn ln(x: Decimal) -> Decimal; - /// Return the natural log of the floating-point number. fn ln(x: float) -> float; -/// Return the log of the decimal number with base 10. -fn log(x: Decimal) -> Decimal; - /// Return the log of the floating-point number with base 10. fn log(x: float) -> float; @@ -3422,17 +3420,6 @@ fn parse_be_int(blob: Blob, range: RangeInclusive) -> int; /// ``` fn parse_be_int(blob: Blob, start: int, len: int) -> int; -/// Parse a string into a decimal number. -/// -/// # Example -/// -/// ```rhai -/// let x = parse_decimal("123.456"); -/// -/// print(x); // prints 123.456 -/// ``` -fn parse_decimal(string: String) -> Decimal; - /// Parse a string into a floating-point number. /// /// # Example @@ -3472,6 +3459,17 @@ fn parse_int(string: String) -> int; /// ``` fn parse_int(string: String, radix: int) -> int; +/// Parse a JSON string into a value. +/// +/// # Example +/// +/// ```rhai +/// let m = parse_json(`{"a":1, "b":2, "c":3}`); +/// +/// print(m); // prints #{"a":1, "b":2, "c":3} +/// ``` +fn parse_json(json: String) -> ?; + /// Parse the bytes within an exclusive `range` in the BLOB as a `FLOAT` /// in little-endian byte order. /// @@ -3621,34 +3619,34 @@ fn pop(string: String) -> ?; fn pop(string: String, len: int) -> String; /// Return the empty string. -fn print() -> String; +op print() -> String; /// Convert the array into a string. -fn print(array: Array) -> String; +op print(Array) -> String; /// Return the character into a string. -fn print(character: char) -> String; +op print(char) -> String; /// Convert the value of the `item` into a string. -fn print(item: ?) -> String; +op print(?) -> String; /// Convert the object map into a string. -fn print(map: Map) -> String; +op print(Map) -> String; /// Convert the value of `number` into a string. -fn print(number: f32) -> String; +op print(f32) -> String; /// Convert the value of `number` into a string. -fn print(number: float) -> String; +op print(float) -> String; /// Return the `string`. -fn print(string: String) -> String; +op print(String) -> String; /// Return the empty string. -fn print(unit: ()) -> String; +op print(()) -> String; /// Return the boolean value into a string. -fn print(value: bool) -> String; +op print(bool) -> String; /// Add a new element, which is not another array, to the end of the array. /// @@ -3810,27 +3808,6 @@ fn range(from: u64, to: u64) -> Iterator; /// ``` fn range(from: u8, to: u8) -> Iterator; -/// Return an iterator over an exclusive range, each iteration increasing by `step`. -/// -/// If `range` is reversed and `step` < 0, iteration goes backwards. -/// -/// Otherwise, if `range` is empty, an empty iterator is returned. -/// -/// # Example -/// -/// ```rhai -/// // prints all values from 8 to 17 in steps of 3 -/// for n in range(8..18, 3) { -/// print(n); -/// } -/// -/// // prints all values down from 18 to 9 in steps of -3 -/// for n in range(18..8, -3) { -/// print(n); -/// } -/// ``` -fn range(range: Range, step: Decimal) -> Iterator; - /// Return an iterator over an exclusive range, each iteration increasing by `step`. /// /// If `range` is reversed and `step` < 0, iteration goes backwards. @@ -4062,28 +4039,6 @@ fn range(range: Range, step: u64) -> Iterator; /// ``` fn range(range: Range, step: u8) -> Iterator; -/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`. -/// The value `to` is never included. -/// -/// If `from` > `to` and `step` < 0, iteration goes backwards. -/// -/// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned. -/// -/// # Example -/// -/// ```rhai -/// // prints all values from 8 to 17 in steps of 3 -/// for n in range(8, 18, 3) { -/// print(n); -/// } -/// -/// // prints all values down from 18 to 9 in steps of -3 -/// for n in range(18, 8, -3) { -/// print(n); -/// } -/// ``` -fn range(from: Decimal, to: Decimal, step: Decimal) -> Iterator; - /// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`. /// The value `to` is never included. /// @@ -4917,34 +4872,10 @@ fn reverse(array: Array) -> (); /// ``` fn reverse(blob: Blob) -> (); -/// Return the nearest whole number closest to the decimal number. -/// Always round mid-point towards the closest even number. -fn round(x: Decimal) -> Decimal; - /// Return the nearest whole number closest to the floating-point number. /// Rounds away from zero. fn round(x: float) -> float; -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round mid-point towards the closest even number. -fn round(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round towards zero. -fn round_down(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round mid-points towards zero. -fn round_half_down(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round mid-points away from zero. -fn round_half_up(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round away from zero. -fn round_up(x: Decimal, digits: int) -> Decimal; - /// Set the element at the `index` position in the array to a new `value`. /// /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). @@ -5170,13 +5101,6 @@ fn shift(array: Array) -> ?; /// ``` fn shift(blob: Blob) -> int; -/// Return the sign (as an integer) of the decimal number according to the following: -/// -/// * `0` if the number is zero -/// * `1` if the number is positive -/// * `-1` if the number is negative -fn sign(x: Decimal) -> int; - /// Return the sign (as an integer) of the number according to the following: /// /// * `0` if the number is zero @@ -5226,9 +5150,6 @@ fn sign(x: i32) -> int; /// * `-1` if the number is negative fn sign(x: i8) -> int; -/// Return the sine of the decimal number in radians. -fn sin(x: Decimal) -> Decimal; - /// Return the sine of the floating-point number in radians. fn sin(x: float) -> float; @@ -5658,9 +5579,6 @@ fn split_rev(string: String, delimiter: String, segments: int) -> Array; /// ``` fn split_rev(string: String, delimiter: char, segments: int) -> Array; -/// Return the square root of the decimal number. -fn sqrt(x: Decimal) -> Decimal; - /// Return the square root of the floating-point number. fn sqrt(x: float) -> float; @@ -5755,9 +5673,6 @@ fn sub_string(string: String, start: int, len: int) -> String; /// ``` fn tag(value: ?) -> int; -/// Return the tangent of the decimal number in radians. -fn tan(x: Decimal) -> Decimal; - /// Return the tangent of the floating-point number in radians. fn tan(x: float) -> float; @@ -5874,34 +5789,9 @@ fn to_debug(unit: ()) -> String; /// Convert the boolean value into a string in debug format. fn to_debug(value: bool) -> String; -/// Convert the floating-point number to decimal. -fn to_decimal(x: f32) -> Decimal; - -/// Convert the floating-point number to decimal. -fn to_decimal(x: float) -> Decimal; - -fn to_decimal(x: i16) -> Decimal; - -fn to_decimal(x: i32) -> Decimal; - -fn to_decimal(x: int) -> Decimal; - -fn to_decimal(x: i8) -> Decimal; - -fn to_decimal(x: u16) -> Decimal; - -fn to_decimal(x: u32) -> Decimal; - -fn to_decimal(x: u64) -> Decimal; - -fn to_decimal(x: u8) -> Decimal; - /// Convert radians to degrees. fn to_degrees(x: float) -> float; -/// Convert the decimal number to floating-point. -fn to_float(x: Decimal) -> float; - /// Convert the 32-bit floating-point number to 64-bit. fn to_float(x: f32) -> float; @@ -5953,9 +5843,6 @@ fn to_hex(value: u64) -> String; /// Convert the `value` into a string in hex format. fn to_hex(value: u8) -> String; -/// Convert the decimal number into an integer. -fn to_int(x: Decimal) -> int; - fn to_int(x: char) -> int; /// Convert the floating-point number into an integer. diff --git a/examples/definitions/.rhai/definitions/__static__.d.rhai b/examples/definitions/.rhai/definitions/__static__.d.rhai index a1876713..9a1e2f7a 100644 --- a/examples/definitions/.rhai/definitions/__static__.d.rhai +++ b/examples/definitions/.rhai/definitions/__static__.d.rhai @@ -159,8 +159,6 @@ op **(u64, int) -> u64; op **(u8, int) -> u8; -op +(Decimal) -> Decimal; - op +(int) -> int; op +(f32) -> f32; @@ -283,8 +281,6 @@ op +=(Instant, float) -> (); /// Add the specified number of `seconds` to the timestamp. op +=(Instant, int) -> (); -op -(Decimal) -> Decimal; - op -(int) -> int; op -(f32) -> f32; @@ -613,9 +609,6 @@ op ^(u64, u64) -> u64; op ^(u8, u8) -> u8; -/// Return the absolute value of the decimal number. -fn abs(x: Decimal) -> Decimal; - /// Return the absolute value of the number. fn abs(x: int) -> int; @@ -788,13 +781,6 @@ fn atan(x: float, y: float) -> float; /// Return the arc-hyperbolic-tangent of the floating-point number, in radians. fn atanh(x: float) -> float; -/// Get an array of object maps containing the function calls stack. -/// -/// If there is no debugging interface registered, an empty array is returned. -/// -/// An array of strings is returned under `no_object`. -fn back_trace() -> Array; - /// Return an iterator over all the bits in the number. /// /// # Example @@ -908,9 +894,6 @@ fn blob(len: int, value: int) -> Blob; /// ``` fn bytes(string: String) -> int; -/// Return the smallest whole number larger than or equals to the decimal number. -fn ceiling(x: Decimal) -> Decimal; - /// Return the smallest whole number larger than or equals to the floating-point number. fn ceiling(x: float) -> float; @@ -1052,8 +1035,63 @@ fn clear(string: String) -> (); /// ``` fn contains(array: Array, value: ?) -> bool; -/// Return the cosine of the decimal number in radians. -fn cos(x: Decimal) -> Decimal; +/// Return `true` if the BLOB contains a specified byte value. +/// +/// # Example +/// +/// ```rhai +/// let text = "hello, world!"; +/// +/// print(text.contains('h')); // prints true +/// +/// print(text.contains('x')); // prints false +/// ``` +fn contains(blob: Blob, value: int) -> bool; + +/// Returns `true` if the object map contains a specified property. +/// +/// # Example +/// +/// ```rhai +/// let m = #{a: 1, b: 2, c: 3}; +/// +/// print(m.contains("b")); // prints true +/// +/// print(m.contains("x")); // prints false +/// ``` +fn contains(map: Map, property: String) -> bool; + +/// Return `true` if the range contains a specified value. +fn contains(range: ExclusiveRange, value: int) -> bool; + +/// Return `true` if the range contains a specified value. +fn contains(range: InclusiveRange, value: int) -> bool; + +/// Return `true` if the string contains a specified character. +/// +/// # Example +/// +/// ```rhai +/// let text = "hello, world!"; +/// +/// print(text.contains('h')); // prints true +/// +/// print(text.contains('x')); // prints false +/// ``` +fn contains(string: String, character: char) -> bool; + +/// Return `true` if the string contains a specified string. +/// +/// # Example +/// +/// ```rhai +/// let text = "hello, world!"; +/// +/// print(text.contains("hello")); // prints true +/// +/// print(text.contains("hey")); // prints false +/// ``` +fn contains(string: String, match_string: String) -> bool; /// Return the cosine of the floating-point number in radians. fn cos(x: float) -> float; @@ -1132,37 +1170,37 @@ fn crop(string: String, start: int) -> (); fn crop(string: String, start: int, len: int) -> (); /// Return the empty string. -fn debug() -> String; +op debug() -> String; /// Convert the array into a string. -fn debug(array: Array) -> String; +op debug(Array) -> String; /// Convert the string into debug format. -fn debug(character: char) -> String; +op debug(char) -> String; /// Convert the function pointer into a string in debug format. -fn debug(f: FnPtr) -> String; +op debug(FnPtr) -> String; /// Convert the value of the `item` into a string in debug format. -fn debug(item: ?) -> String; +op debug(?) -> String; /// Convert the object map into a string. -fn debug(map: Map) -> String; +op debug(Map) -> String; /// Convert the value of `number` into a string. -fn debug(number: f32) -> String; +op debug(f32) -> String; /// Convert the value of `number` into a string. -fn debug(number: float) -> String; +op debug(float) -> String; /// Convert the string into debug format. -fn debug(string: String) -> String; +op debug(String) -> String; /// Convert the unit into a string in debug format. -fn debug(unit: ()) -> String; +op debug(()) -> String; /// Convert the boolean value into a string in debug format. -fn debug(value: bool) -> String; +op debug(bool) -> String; /// Remove duplicated _consecutive_ elements from the array. /// @@ -1468,9 +1506,6 @@ fn end(range: InclusiveRange) -> int; /// ``` fn ends_with(string: String, match_string: String) -> bool; -/// Return the exponential of the decimal number. -fn exp(x: Decimal) -> Decimal; - /// Return the exponential of the floating-point number. fn exp(x: float) -> float; @@ -1681,15 +1716,9 @@ fn filter(array: Array, filter: FnPtr) -> Array; /// ``` fn filter(array: Array, filter_func: String) -> Array; -/// Return the largest whole number less than or equals to the decimal number. -fn floor(x: Decimal) -> Decimal; - /// Return the largest whole number less than or equals to the floating-point number. fn floor(x: float) -> float; -/// Return the fractional part of the decimal number. -fn fraction(x: Decimal) -> Decimal; - /// Return the fractional part of the floating-point number. fn fraction(x: float) -> float; @@ -1791,9 +1820,6 @@ fn get bits(value: int) -> Iterator; /// ``` fn get bytes(string: String) -> int; -/// Return the smallest whole number larger than or equals to the decimal number. -fn get ceiling(x: Decimal) -> Decimal; - /// Return the smallest whole number larger than or equals to the floating-point number. fn get ceiling(x: float) -> float; @@ -1827,21 +1853,12 @@ fn get end(range: ExclusiveRange) -> int; /// Return the end of the inclusive range. fn get end(range: InclusiveRange) -> int; -/// Return the largest whole number less than or equals to the decimal number. -fn get floor(x: Decimal) -> Decimal; - /// Return the largest whole number less than or equals to the floating-point number. fn get floor(x: float) -> float; -/// Return the fractional part of the decimal number. -fn get fraction(x: Decimal) -> Decimal; - /// Return the fractional part of the floating-point number. fn get fraction(x: float) -> float; -/// Return the integral part of the decimal number. -fn get int(x: Decimal) -> Decimal; - /// Return the integral part of the floating-point number. fn get int(x: float) -> float; @@ -1952,9 +1969,6 @@ fn get is_odd(x: u64) -> bool; /// Return true if the number is odd. fn get is_odd(x: u8) -> bool; -/// Return true if the decimal number is zero. -fn get is_zero(x: Decimal) -> bool; - /// Return true if the number is zero. fn get is_zero(x: int) -> bool; @@ -2031,10 +2045,6 @@ fn get len(string: String) -> int; /// ``` fn get name(fn_ptr: FnPtr) -> String; -/// Return the nearest whole number closest to the decimal number. -/// Always round mid-point towards the closest even number. -fn get round(x: Decimal) -> Decimal; - /// Return the nearest whole number closest to the floating-point number. /// Rounds away from zero. fn get round(x: float) -> float; @@ -2399,9 +2409,6 @@ fn insert(array: Array, index: int, item: ?) -> (); /// ``` fn insert(blob: Blob, index: int, value: int) -> (); -/// Return the integral part of the decimal number. -fn int(x: Decimal) -> Decimal; - /// Return the integral part of the floating-point number. fn int(x: float) -> float; @@ -2515,9 +2522,6 @@ fn is_odd(x: u64) -> bool; /// Return true if the number is odd. fn is_odd(x: u8) -> bool; -/// Return true if the decimal number is zero. -fn is_zero(x: Decimal) -> bool; - /// Return true if the number is zero. fn is_zero(x: int) -> bool; @@ -2595,15 +2599,9 @@ fn len(map: Map) -> int; /// ``` fn len(string: String) -> int; -/// Return the natural log of the decimal number. -fn ln(x: Decimal) -> Decimal; - /// Return the natural log of the floating-point number. fn ln(x: float) -> float; -/// Return the log of the decimal number with base 10. -fn log(x: Decimal) -> Decimal; - /// Return the log of the floating-point number with base 10. fn log(x: float) -> float; @@ -2904,17 +2902,6 @@ fn parse_be_int(blob: Blob, range: RangeInclusive) -> int; /// ``` fn parse_be_int(blob: Blob, start: int, len: int) -> int; -/// Parse a string into a decimal number. -/// -/// # Example -/// -/// ```rhai -/// let x = parse_decimal("123.456"); -/// -/// print(x); // prints 123.456 -/// ``` -fn parse_decimal(string: String) -> Decimal; - /// Parse a string into a floating-point number. /// /// # Example @@ -2954,6 +2941,17 @@ fn parse_int(string: String) -> int; /// ``` fn parse_int(string: String, radix: int) -> int; +/// Parse a JSON string into a value. +/// +/// # Example +/// +/// ```rhai +/// let m = parse_json(`{"a":1, "b":2, "c":3}`); +/// +/// print(m); // prints #{"a":1, "b":2, "c":3} +/// ``` +fn parse_json(json: String) -> ?; + /// Parse the bytes within an exclusive `range` in the BLOB as a `FLOAT` /// in little-endian byte order. /// @@ -3103,34 +3101,34 @@ fn pop(string: String) -> ?; fn pop(string: String, len: int) -> String; /// Return the empty string. -fn print() -> String; +op print() -> String; /// Convert the array into a string. -fn print(array: Array) -> String; +op print(Array) -> String; /// Return the character into a string. -fn print(character: char) -> String; +op print(char) -> String; /// Convert the value of the `item` into a string. -fn print(item: ?) -> String; +op print(?) -> String; /// Convert the object map into a string. -fn print(map: Map) -> String; +op print(Map) -> String; /// Convert the value of `number` into a string. -fn print(number: f32) -> String; +op print(f32) -> String; /// Convert the value of `number` into a string. -fn print(number: float) -> String; +op print(float) -> String; /// Return the `string`. -fn print(string: String) -> String; +op print(String) -> String; /// Return the empty string. -fn print(unit: ()) -> String; +op print(()) -> String; /// Return the boolean value into a string. -fn print(value: bool) -> String; +op print(bool) -> String; /// Add a new element, which is not another array, to the end of the array. /// @@ -3292,27 +3290,6 @@ fn range(from: u64, to: u64) -> Iterator; /// ``` fn range(from: u8, to: u8) -> Iterator; -/// Return an iterator over an exclusive range, each iteration increasing by `step`. -/// -/// If `range` is reversed and `step` < 0, iteration goes backwards. -/// -/// Otherwise, if `range` is empty, an empty iterator is returned. -/// -/// # Example -/// -/// ```rhai -/// // prints all values from 8 to 17 in steps of 3 -/// for n in range(8..18, 3) { -/// print(n); -/// } -/// -/// // prints all values down from 18 to 9 in steps of -3 -/// for n in range(18..8, -3) { -/// print(n); -/// } -/// ``` -fn range(range: Range, step: Decimal) -> Iterator; - /// Return an iterator over an exclusive range, each iteration increasing by `step`. /// /// If `range` is reversed and `step` < 0, iteration goes backwards. @@ -3544,28 +3521,6 @@ fn range(range: Range, step: u64) -> Iterator; /// ``` fn range(range: Range, step: u8) -> Iterator; -/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`. -/// The value `to` is never included. -/// -/// If `from` > `to` and `step` < 0, iteration goes backwards. -/// -/// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned. -/// -/// # Example -/// -/// ```rhai -/// // prints all values from 8 to 17 in steps of 3 -/// for n in range(8, 18, 3) { -/// print(n); -/// } -/// -/// // prints all values down from 18 to 9 in steps of -3 -/// for n in range(18, 8, -3) { -/// print(n); -/// } -/// ``` -fn range(from: Decimal, to: Decimal, step: Decimal) -> Iterator; - /// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`. /// The value `to` is never included. /// @@ -4399,34 +4354,10 @@ fn reverse(array: Array) -> (); /// ``` fn reverse(blob: Blob) -> (); -/// Return the nearest whole number closest to the decimal number. -/// Always round mid-point towards the closest even number. -fn round(x: Decimal) -> Decimal; - /// Return the nearest whole number closest to the floating-point number. /// Rounds away from zero. fn round(x: float) -> float; -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round mid-point towards the closest even number. -fn round(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round towards zero. -fn round_down(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round mid-points towards zero. -fn round_half_down(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round mid-points away from zero. -fn round_half_up(x: Decimal, digits: int) -> Decimal; - -/// Round the decimal number to the specified number of `digits` after the decimal point and return it. -/// Always round away from zero. -fn round_up(x: Decimal, digits: int) -> Decimal; - /// Set the element at the `index` position in the array to a new `value`. /// /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). @@ -4652,13 +4583,6 @@ fn shift(array: Array) -> ?; /// ``` fn shift(blob: Blob) -> int; -/// Return the sign (as an integer) of the decimal number according to the following: -/// -/// * `0` if the number is zero -/// * `1` if the number is positive -/// * `-1` if the number is negative -fn sign(x: Decimal) -> int; - /// Return the sign (as an integer) of the number according to the following: /// /// * `0` if the number is zero @@ -4708,9 +4632,6 @@ fn sign(x: i32) -> int; /// * `-1` if the number is negative fn sign(x: i8) -> int; -/// Return the sine of the decimal number in radians. -fn sin(x: Decimal) -> Decimal; - /// Return the sine of the floating-point number in radians. fn sin(x: float) -> float; @@ -5140,9 +5061,6 @@ fn split_rev(string: String, delimiter: String, segments: int) -> Array; /// ``` fn split_rev(string: String, delimiter: char, segments: int) -> Array; -/// Return the square root of the decimal number. -fn sqrt(x: Decimal) -> Decimal; - /// Return the square root of the floating-point number. fn sqrt(x: float) -> float; @@ -5237,9 +5155,6 @@ fn sub_string(string: String, start: int, len: int) -> String; /// ``` fn tag(value: ?) -> int; -/// Return the tangent of the decimal number in radians. -fn tan(x: Decimal) -> Decimal; - /// Return the tangent of the floating-point number in radians. fn tan(x: float) -> float; @@ -5356,34 +5271,9 @@ fn to_debug(unit: ()) -> String; /// Convert the boolean value into a string in debug format. fn to_debug(value: bool) -> String; -/// Convert the floating-point number to decimal. -fn to_decimal(x: f32) -> Decimal; - -/// Convert the floating-point number to decimal. -fn to_decimal(x: float) -> Decimal; - -fn to_decimal(x: i16) -> Decimal; - -fn to_decimal(x: i32) -> Decimal; - -fn to_decimal(x: int) -> Decimal; - -fn to_decimal(x: i8) -> Decimal; - -fn to_decimal(x: u16) -> Decimal; - -fn to_decimal(x: u32) -> Decimal; - -fn to_decimal(x: u64) -> Decimal; - -fn to_decimal(x: u8) -> Decimal; - /// Convert radians to degrees. fn to_degrees(x: float) -> float; -/// Convert the decimal number to floating-point. -fn to_float(x: Decimal) -> float; - /// Convert the 32-bit floating-point number to 64-bit. fn to_float(x: f32) -> float; @@ -5435,9 +5325,6 @@ fn to_hex(value: u64) -> String; /// Convert the `value` into a string in hex format. fn to_hex(value: u8) -> String; -/// Convert the decimal number into an integer. -fn to_int(x: Decimal) -> int; - fn to_int(x: char) -> int; /// Convert the floating-point number into an integer. diff --git a/examples/definitions/.rhai/defs.json b/examples/definitions/.rhai/defs.json index bb25ab3e..467a5507 100644 --- a/examples/definitions/.rhai/defs.json +++ b/examples/definitions/.rhai/defs.json @@ -3,8 +3,8 @@ "general_kenobi": { "functions": [ { - "baseHash": 727795846011184342, - "fullHash": 5101524478338862216, + "baseHash": 3873007749982070651, + "fullHash": 5865213555928423624, "namespace": "internal", "access": "public", "name": "hello_there", @@ -27,8 +27,8 @@ }, "functions": [ { - "baseHash": 17133166385977770750, - "fullHash": 11299449021188202345, + "baseHash": 12461724250411739075, + "fullHash": 14530626537296006176, "namespace": "global", "access": "public", "name": "minus", diff --git a/src/config/hashing.rs b/src/config/hashing.rs index 102586b0..dd93e812 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -160,13 +160,13 @@ where } } -unsafe impl Sync for SusLock where T: 'static + Copy {} -unsafe impl Send for SusLock where T: 'static + Copy {} -impl RefUnwindSafe for SusLock where T: 'static + Copy {} +unsafe impl Sync for SusLock where T: 'static {} +unsafe impl Send for SusLock where T: 'static {} +impl RefUnwindSafe for SusLock where T: 'static {} impl Drop for SusLock where - T: 'static + Copy, + T: 'static, { #[inline] fn drop(&mut self) { @@ -217,8 +217,10 @@ pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]> #[inline] #[must_use] pub fn get_ahash_seed() -> &'static Option<[u64; 4]> { + const NONE: &'static Option<[u64; 4]> = &None; + match AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED) { Some(ash) => ash, - None => None, + None => NONE, } }