From 92c7fd9e5b90b3f3c321529b603afe06b664deeb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 15 Jan 2021 17:13:04 +0800 Subject: [PATCH 01/12] Better error messages. --- src/fn_call.rs | 4 ++-- src/result.rs | 5 +++-- src/token.rs | 20 ++++++++++---------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 21bc570c..5ee9387f 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -269,7 +269,7 @@ impl Engine { if let Some(prop) = extract_prop_from_getter(fn_name) { return EvalAltResult::ErrorDotExpr( format!( - "Failed to get property '{}' of '{}' - the property may not exist, or it may be write-only", + "Unknown property '{}' - a getter is not registered for type '{}'", prop, self.map_type_name(args[0].type_name()) ), @@ -283,7 +283,7 @@ impl Engine { if let Some(prop) = extract_prop_from_setter(fn_name) { return EvalAltResult::ErrorDotExpr( format!( - "Failed to set property '{}' of '{}' - the property may not exist, may be read-only, or '{}' is the wrong type", + "No writable property '{}' - a setter is not registered for type '{}' to handle '{}'", prop, self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), diff --git a/src/result.rs b/src/result.rs index d0ca7e52..39a0b315 100644 --- a/src/result.rs +++ b/src/result.rs @@ -178,8 +178,9 @@ impl fmt::Display for EvalAltResult { Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{}", s)?, - Self::ErrorIndexingType(_, _) - | Self::ErrorUnboundThis(_) + Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for type '{}'", s)?, + + Self::ErrorUnboundThis(_) | Self::ErrorFor(_) | Self::ErrorInExpr(_) | Self::ErrorDotExpr(_, _) diff --git a/src/token.rs b/src/token.rs index de291fbe..93eee9a2 100644 --- a/src/token.rs +++ b/src/token.rs @@ -768,10 +768,13 @@ pub struct TokenizeState { /// /// This trait is volatile and may change. pub trait InputStream { - fn unread(&mut self, ch: char); - /// Get the next character + /// Un-get a character back into the `InputStream`. + /// The next [`get_next`][InputStream::get_next] or [`peek_next`][InputStream::peek_next] + /// will return this character instead. + fn unget(&mut self, ch: char); + /// Get the next character from the `InputStream`. fn get_next(&mut self) -> Option; - /// Peek the next character + /// Peek the next character in the `InputStream`. fn peek_next(&mut self) -> Option; } @@ -1088,12 +1091,12 @@ fn get_next_token_inner( } // _ - cannot follow a decimal point '_' => { - stream.unread(next_char); + stream.unget(next_char); break; } // .. - reserved symbol, not a floating-point number '.' => { - stream.unread(next_char); + stream.unget(next_char); break; } // symbol after period - probably a float @@ -1104,7 +1107,7 @@ fn get_next_token_inner( } // Not a floating-point number _ => { - stream.unread(next_char); + stream.unget(next_char); break; } } @@ -1634,12 +1637,10 @@ pub struct MultiInputsStream<'a> { } impl InputStream for MultiInputsStream<'_> { - /// Buffer a character. #[inline(always)] - fn unread(&mut self, ch: char) { + fn unget(&mut self, ch: char) { self.buf = Some(ch); } - /// Get the next character fn get_next(&mut self) -> Option { if let Some(ch) = self.buf.take() { return Some(ch); @@ -1658,7 +1659,6 @@ impl InputStream for MultiInputsStream<'_> { } } } - /// Peek the next character fn peek_next(&mut self) -> Option { if let Some(ch) = self.buf { return Some(ch); From 75bb5ef650b8820a522a97f200dc172d0b45ee76 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 15 Jan 2021 17:13:24 +0800 Subject: [PATCH 02/12] Bump version. --- Cargo.toml | 2 +- RELEASES.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cb68a5e0..0420698c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "rhai" -version = "0.19.10" +version = "0.19.11" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index 1240a3cf..e1445d1e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,11 @@ Rhai Release Notes ================== +Version 0.19.11 +=============== + + + Version 0.19.10 =============== From 9e2c1a5860e172edb636ad3017d7be3030945f2f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 15 Jan 2021 19:55:14 +0800 Subject: [PATCH 03/12] Bump version. --- codegen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 073843b1..3088cdab 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "0.3.1" +version = "0.3.2" edition = "2018" authors = ["jhwgh1968"] description = "Procedural macro support package for Rhai, a scripting language for Rust" From 1c7884f0d8a1b311d9554b251f94b1269bd1a79b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 15 Jan 2021 23:30:45 +0800 Subject: [PATCH 04/12] Switch to fxhash for fast hashing. --- Cargo.toml | 3 ++- README.md | 2 +- src/utils.rs | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0420698c..b5396f99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ keywords = [ "scripting" ] categories = [ "no-std", "embedded", "wasm", "parser-implementations" ] [dependencies] -smallvec = { version = "1.4.2", default-features = false } +smallvec = { version = "1.4", default-features = false } +fxhash = { version = "0.2" } rhai_codegen = { version = "0.3", path = "codegen" } [features] diff --git a/README.md b/README.md index 82aa7dec..32d52752 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Standard features * Freely pass Rust variables/constants into a script via an external [`Scope`](https://rhaiscript.github.io/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait. * Easily [call a script-defined function](https://rhaiscript.github.io/book/engine/call-fn.html) from Rust. * Relatively little `unsafe` code (yes there are some for performance reasons). -* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec)). +* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec) and [`fxhash`](https://crates.io/crates/fxhash)). * Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature). * Scripts are [optimized](https://rhaiscript.github.io/book/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations. * Easy custom API development via [plugins](https://rhaiscript.github.io/book/plugins/index.html) system powered by procedural macros. diff --git a/src/utils.rs b/src/utils.rs index 6931476a..e92bcefa 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -61,8 +61,10 @@ impl BuildHasher for StraightHasherBuilder { pub fn get_hasher() -> impl Hasher { #[cfg(feature = "no_std")] let s: ahash::AHasher = Default::default(); + // #[cfg(not(feature = "no_std"))] + // let s = crate::stdlib::collections::hash_map::DefaultHasher::new(); #[cfg(not(feature = "no_std"))] - let s = crate::stdlib::collections::hash_map::DefaultHasher::new(); + let s: fxhash::FxHasher64 = Default::default(); s } From c4b6c31bf0b42c740b68b5d623ce76218b024f0d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 15 Jan 2021 23:52:56 +0800 Subject: [PATCH 05/12] Use ahash as default hasher. --- Cargo.toml | 10 ++-------- src/utils.rs | 6 ------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5396f99..89e2cca7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ] [dependencies] smallvec = { version = "1.4", default-features = false } -fxhash = { version = "0.2" } +ahash = { version = "0.3.2", default-features = false, features = ["compile-time-rng"] } rhai_codegen = { version = "0.3", path = "codegen" } [features] @@ -46,7 +46,7 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident metadata = [ "serde", "serde_json"] # enables exporting functions metadata to JSON # compiling for no-std -no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ] +no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm" ] [profile.release] lto = "fat" @@ -76,12 +76,6 @@ default-features = false features = ["ahash", "nightly", "inline-more"] optional = true -[dependencies.ahash] -version = "0.3.2" -default-features = false -features = ["compile-time-rng"] -optional = true - [dependencies.serde] version = "1.0.116" default_features = false diff --git a/src/utils.rs b/src/utils.rs index e92bcefa..7cbed35c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,13 +59,7 @@ impl BuildHasher for StraightHasherBuilder { /// Create an instance of the default hasher. #[inline(always)] pub fn get_hasher() -> impl Hasher { - #[cfg(feature = "no_std")] let s: ahash::AHasher = Default::default(); - // #[cfg(not(feature = "no_std"))] - // let s = crate::stdlib::collections::hash_map::DefaultHasher::new(); - #[cfg(not(feature = "no_std"))] - let s: fxhash::FxHasher64 = Default::default(); - s } From 421baebbc130e3b4a4b95c25f8108a177012bf8b Mon Sep 17 00:00:00 2001 From: Ilya Lakhin Date: Sat, 16 Jan 2021 01:38:50 +0700 Subject: [PATCH 06/12] Exposing AST functions shared module under internals feature --- src/ast.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ast.rs b/src/ast.rs index 12bf72ec..c68555e9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -266,12 +266,21 @@ impl AST { &mut self.statements } /// Get the internal shared [`Module`] containing all script-defined functions. + #[cfg(not(feature = "internals"))] #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[inline(always)] pub(crate) fn shared_lib(&self) -> Shared { self.functions.clone() } + /// Get the internal shared [`Module`] containing all script-defined functions. + #[cfg(feature = "internals")] + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub fn shared_lib(&self) -> Shared { + self.functions.clone() + } /// Get the internal [`Module`] containing all script-defined functions. #[cfg(not(feature = "internals"))] #[inline(always)] From def1e7fe143bb9cfb477c5760f2a41c28504cca8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 16 Jan 2021 11:26:44 +0800 Subject: [PATCH 07/12] Use smallvec/union and update ahash. --- Cargo.toml | 22 +++++++++++----------- README.md | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89e2cca7..cc9e2051 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,8 @@ keywords = [ "scripting" ] categories = [ "no-std", "embedded", "wasm", "parser-implementations" ] [dependencies] -smallvec = { version = "1.4", default-features = false } -ahash = { version = "0.3.2", default-features = false, features = ["compile-time-rng"] } +smallvec = { version = "1.6", default-features = false, features = ["union"] } +ahash = { version = "0.5", default-features = false } rhai_codegen = { version = "0.3", path = "codegen" } [features] @@ -46,7 +46,7 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident metadata = [ "serde", "serde_json"] # enables exporting functions metadata to JSON # compiling for no-std -no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm" ] +no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash/compile-time-rng" ] [profile.release] lto = "fat" @@ -55,46 +55,46 @@ codegen-units = 1 #panic = 'abort' # remove stack backtrace for no-std [dependencies.libm] -version = "0.2.1" +version = "0.2" default_features = false optional = true [dependencies.num-traits] -version = "0.2.11" +version = "0.2" default-features = false optional = true [dependencies.core-error] -version = "0.0.0" +version = "0.0" default_features = false features = ["alloc"] optional = true [dependencies.hashbrown] -version = "0.7.1" +version = "0.7" default-features = false features = ["ahash", "nightly", "inline-more"] optional = true [dependencies.serde] -version = "1.0.116" +version = "1.0" default_features = false features = ["derive", "alloc"] optional = true [dependencies.serde_json] -version = "1.0.60" +version = "1.0" default_features = false features = ["alloc"] optional = true [dependencies.unicode-xid] -version = "0.2.1" +version = "0.2" default_features = false optional = true [target.'cfg(target_arch = "wasm32")'.dependencies] -instant= { version = "0.1.7", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant +instant= { version = "0.1", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant [package.metadata.docs.rs] features = [ "serde", "internals" ] diff --git a/README.md b/README.md index 32d52752..8f5f82e9 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Standard features * Freely pass Rust variables/constants into a script via an external [`Scope`](https://rhaiscript.github.io/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait. * Easily [call a script-defined function](https://rhaiscript.github.io/book/engine/call-fn.html) from Rust. * Relatively little `unsafe` code (yes there are some for performance reasons). -* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec) and [`fxhash`](https://crates.io/crates/fxhash)). +* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec) and [`ahash`](https://crates.io/crates/ahash)). * Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature). * Scripts are [optimized](https://rhaiscript.github.io/book/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations. * Easy custom API development via [plugins](https://rhaiscript.github.io/book/plugins/index.html) system powered by procedural macros. From 65621b79b16f6ee161a54cd11af2d381096d49d4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 16 Jan 2021 14:46:03 +0800 Subject: [PATCH 08/12] Fix volatile warnings. --- src/ast.rs | 22 ++++++++++++---------- src/engine.rs | 6 +++--- src/module/mod.rs | 2 +- src/parse_error.rs | 2 +- src/token.rs | 14 +++++++------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index c68555e9..0a69dd17 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -60,7 +60,7 @@ impl FnAccess { /// _(INTERNALS)_ A type containing information on a scripted function. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone)] @@ -273,8 +273,10 @@ impl AST { pub(crate) fn shared_lib(&self) -> Shared { self.functions.clone() } - /// Get the internal shared [`Module`] containing all script-defined functions. + /// _(INTERNALS)_ Get the internal shared [`Module`] containing all script-defined functions. + /// Exported under the `internals` feature only. #[cfg(feature = "internals")] + #[deprecated = "this method is volatile and may change"] #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[inline(always)] @@ -764,7 +766,7 @@ impl AsRef for AST { /// _(INTERNALS)_ An identifier containing an [immutable string][ImmutableString] name and a [position][Position]. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Clone, Eq, PartialEq, Hash)] @@ -785,7 +787,7 @@ impl fmt::Debug for Ident { /// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] @@ -799,7 +801,7 @@ pub enum ReturnType { /// _(INTERNALS)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`]. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Hash)] @@ -823,7 +825,7 @@ impl<'a> From<&'a Expr> for ASTNode<'a> { /// _(INTERNALS)_ A statement. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Hash)] @@ -1070,7 +1072,7 @@ impl Stmt { /// _(INTERNALS)_ A custom syntax expression. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Hash)] @@ -1086,7 +1088,7 @@ pub struct CustomExpr { /// _(INTERNALS)_ A binary expression. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Hash)] @@ -1100,7 +1102,7 @@ pub struct BinaryExpr { /// _(INTERNALS)_ A function call. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Default, Hash)] @@ -1195,7 +1197,7 @@ impl FloatWrapper { /// _(INTERNALS)_ An expression sub-tree. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Hash)] diff --git a/src/engine.rs b/src/engine.rs index 77f0b8aa..c6dce576 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -45,7 +45,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical /// _(INTERNALS)_ A stack of imported [modules][Module]. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. // @@ -498,7 +498,7 @@ impl> From for Target<'_> { /// _(INTERNALS)_ A type that holds all the current states of the [`Engine`]. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Default)] @@ -538,7 +538,7 @@ impl State { /// _(INTERNALS)_ A type containing all the limits imposed by the [`Engine`]. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[cfg(not(feature = "unchecked"))] diff --git a/src/module/mod.rs b/src/module/mod.rs index 0d9046fe..c145e217 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -2004,7 +2004,7 @@ impl Module { /// A [`StaticVec`] is used because most namespace-qualified access contains only one level, /// and it is wasteful to always allocate a [`Vec`] with one element. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Clone, Eq, PartialEq, Default, Hash)] diff --git a/src/parse_error.rs b/src/parse_error.rs index e42c851a..a965af0b 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -11,7 +11,7 @@ use crate::{EvalAltResult, Position}; /// _(INTERNALS)_ Error encountered when tokenizing the script text. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Eq, PartialEq, Clone, Hash)] diff --git a/src/token.rs b/src/token.rs index 93eee9a2..89da190c 100644 --- a/src/token.rs +++ b/src/token.rs @@ -150,7 +150,7 @@ impl fmt::Debug for Position { /// _(INTERNALS)_ A Rhai language token. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, PartialEq, Clone)] @@ -742,7 +742,7 @@ impl From for String { /// _(INTERNALS)_ State of the tokenizer. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Debug, Clone, Eq, PartialEq, Default)] @@ -764,7 +764,7 @@ pub struct TokenizeState { /// _(INTERNALS)_ Trait that encapsulates a peekable character input stream. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile Data Structure /// /// This trait is volatile and may change. pub trait InputStream { @@ -781,9 +781,9 @@ pub trait InputStream { /// _(INTERNALS)_ Parse a string literal wrapped by `enclosing_char`. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile API /// -/// This type is volatile and may change. +/// This function is volatile and may change. pub fn parse_string_literal( stream: &mut impl InputStream, state: &mut TokenizeState, @@ -976,9 +976,9 @@ fn scan_block_comment( /// _(INTERNALS)_ Get the next token from the `stream`. /// Exported under the `internals` feature only. /// -/// # WARNING +/// # Volatile API /// -/// This type is volatile and may change. +/// This function is volatile and may change. #[inline] pub fn get_next_token( stream: &mut impl InputStream, From 6b5a14ee882fdec71034920825f34727469e6176 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 18 Jan 2021 10:23:41 +0800 Subject: [PATCH 09/12] Fix compilation bug. --- src/serde_impl/metadata.rs | 67 +++++++++++++++----------------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/src/serde_impl/metadata.rs b/src/serde_impl/metadata.rs index 80a6cd01..85a63d87 100644 --- a/src/serde_impl/metadata.rs +++ b/src/serde_impl/metadata.rs @@ -2,7 +2,6 @@ use crate::stdlib::{ cmp::Ordering, collections::BTreeMap, string::{String, ToString}, - vec, vec::Vec, }; use crate::{Engine, AST}; @@ -89,7 +88,7 @@ struct FnMetadata { #[serde(default, skip_serializing_if = "Option::is_none")] pub return_type: Option, pub signature: String, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub doc_comments: Vec, } @@ -125,31 +124,25 @@ impl From<&crate::module::FuncInfo> for FnMetadata { FnType::Native }, num_params: info.params, - params: if let Some(ref names) = info.param_names { - names - .iter() - .take(info.params) - .map(|s| { - let mut seg = s.splitn(2, ':'); - let name = seg - .next() - .map(|s| s.trim().to_string()) - .unwrap_or("_".to_string()); - let typ = seg.next().map(|s| s.trim().to_string()); - FnParam { name, typ } - }) - .collect() - } else { - vec![] - }, - return_type: if let Some(ref names) = info.param_names { - names - .last() - .map(|s| s.to_string()) - .or_else(|| Some("()".to_string())) - } else { - None - }, + params: info + .param_names + .iter() + .take(info.params) + .map(|s| { + let mut seg = s.splitn(2, ':'); + let name = seg + .next() + .map(|s| s.trim().to_string()) + .unwrap_or("_".to_string()); + let typ = seg.next().map(|s| s.trim().to_string()); + FnParam { name, typ } + }) + .collect(), + return_type: info + .param_names + .last() + .map(|s| s.to_string()) + .or_else(|| Some("()".to_string())), signature: info.gen_signature(), doc_comments: if info.func.is_script() { info.func.get_fn_def().comments.clone() @@ -178,11 +171,7 @@ impl From> for FnMetadata { .collect(), return_type: Some("Dynamic".to_string()), signature: info.to_string(), - doc_comments: if info.comments.is_empty() { - None - } else { - Some(info.comments.iter().map(|s| s.to_string()).collect()) - }, + doc_comments: info.comments.iter().map(|s| s.to_string()).collect(), } } } @@ -211,10 +200,10 @@ impl From<&crate::Module> for ModuleMetadata { } } -#[cfg(feature = "serde")] +#[cfg(feature = "metadata")] impl Engine { - /// Generate a list of all functions (including those defined in an [`AST`][crate::AST]) - /// in JSON format. Available only under the `metadata` feature. + /// _(METADATA)_ Generate a list of all functions (including those defined in an + /// [`AST`][crate::AST]) in JSON format. Available only under the `metadata` feature. /// /// Functions from the following sources are included: /// 1) Functions defined in an [`AST`][crate::AST] @@ -244,11 +233,9 @@ impl Engine { .map(|f| f.into()) .for_each(|info| global.functions.push(info)); - if let Some(ast) = ast { - ast.iter_functions() - .map(|f| f.into()) - .for_each(|info| global.functions.push(info)); - } + ast.iter_functions() + .map(|f| f.into()) + .for_each(|info| global.functions.push(info)); global.functions.sort(); From ec272cf9b970504b6eafc8b60a1fbff5ec1fd104 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 18 Jan 2021 10:30:58 +0800 Subject: [PATCH 10/12] Add Serialize/Deserialize for Dynamic and ImmutableString. --- .github/workflows/build.yml | 4 +- Cargo.toml | 2 +- README.md | 2 +- RELEASES.md | 14 ++++ src/serde_impl/de.rs | 14 +--- src/serde_impl/deserialize.rs | 144 ++++++++++++++++++++++++++++++++++ src/serde_impl/mod.rs | 2 + src/serde_impl/ser.rs | 3 +- src/serde_impl/serialize.rs | 55 +++++++++++++ tests/serde.rs | 69 ++++++++++++---- 10 files changed, 278 insertions(+), 31 deletions(-) create mode 100644 src/serde_impl/deserialize.rs create mode 100644 src/serde_impl/serialize.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 493911b5..df6f3b20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: os: [ubuntu-latest] flags: - "" - - "--features serde,internals" + - "--features metadata,internals" - "--features unchecked" - "--features sync" - "--features no_optimize" @@ -32,7 +32,7 @@ jobs: - "--features no_module" - "--features no_closure" - "--features unicode-xid-ident" - - "--features sync,no_function,no_float,no_optimize,no_module,no_closure,serde,unchecked" + - "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,unchecked" toolchain: [stable] experimental: [false] include: diff --git a/Cargo.toml b/Cargo.toml index cc9e2051..ef8767ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,4 +97,4 @@ optional = true instant= { version = "0.1", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant [package.metadata.docs.rs] -features = [ "serde", "internals" ] +features = [ "metadata", "internals" ] diff --git a/README.md b/README.md index 8f5f82e9..83428cf0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Supported targets and builds * All common CPU targets for Windows, Linux and MacOS. * WebAssembly (WASM) * `no-std` -* Minimum Rust version 1.45 +* Minimum Rust version 1.49 Standard features diff --git a/RELEASES.md b/RELEASES.md index e1445d1e..4fcc5001 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,20 @@ Rhai Release Notes Version 0.19.11 =============== +Breaking changes +---------------- + +Rust compiler requirement raised to 1.49. + +Bug fixes +--------- + +* Fixes compilation errors in `metadata` feature build. + +Enhancements +------------ + +* `ahash` is used to hash function call parameters. This should yield speed improvements. Version 0.19.10 diff --git a/src/serde_impl/de.rs b/src/serde_impl/de.rs index e17d177c..eb35ae63 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -4,10 +4,8 @@ use super::str::ImmutableStringDeserializer; use crate::dynamic::Union; use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position}; -use serde::de::{ - DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor, -}; -use serde::Deserialize; +use serde::de::{DeserializeSeed, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor}; +use serde::{Deserialize, Deserializer}; #[cfg(not(feature = "no_index"))] use crate::Array; @@ -50,13 +48,9 @@ impl<'de> DynamicDeserializer<'de> { visitor: V, ) -> Result> { #[cfg(not(feature = "only_i32"))] - { - visitor.visit_i64(v) - } + return visitor.visit_i64(v); #[cfg(feature = "only_i32")] - { - visitor.visit_i32(v) - } + return visitor.visit_i32(v); } } diff --git a/src/serde_impl/deserialize.rs b/src/serde_impl/deserialize.rs new file mode 100644 index 00000000..8d998ad8 --- /dev/null +++ b/src/serde_impl/deserialize.rs @@ -0,0 +1,144 @@ +//! Implementations of [`serde::Deserialize`]. + +use crate::stdlib::{fmt, string::ToString}; +use crate::{Dynamic, ImmutableString, INT}; +use serde::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; + +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_object"))] +use crate::Map; + +struct DynamicVisitor; + +impl<'d> Visitor<'d> for DynamicVisitor { + type Value = Dynamic; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("any type that can be converted into a Dynamic") + } + fn visit_bool(self, v: bool) -> Result { + Ok(v.into()) + } + fn visit_i8(self, v: i8) -> Result { + Ok(INT::from(v).into()) + } + fn visit_i16(self, v: i16) -> Result { + Ok(INT::from(v).into()) + } + fn visit_i32(self, v: i32) -> Result { + Ok(INT::from(v).into()) + } + fn visit_i64(self, v: i64) -> Result { + #[cfg(not(feature = "only_i32"))] + return Ok(v.into()); + #[cfg(feature = "only_i32")] + if v > i32::MAX as i64 { + return Ok(Dynamic::from(v)); + } else { + return self.visit_i32(v as i32); + } + } + fn visit_u8(self, v: u8) -> Result { + Ok(INT::from(v).into()) + } + fn visit_u16(self, v: u16) -> Result { + Ok(INT::from(v).into()) + } + fn visit_u32(self, v: u32) -> Result { + #[cfg(not(feature = "only_i32"))] + return Ok(INT::from(v).into()); + #[cfg(feature = "only_i32")] + if v > i32::MAX as u32 { + return Ok(Dynamic::from(v)); + } else { + return self.visit_i32(v as i32); + } + } + fn visit_u64(self, v: u64) -> Result { + #[cfg(not(feature = "only_i32"))] + if v > i64::MAX as u64 { + return Ok(Dynamic::from(v)); + } else { + return self.visit_i64(v as i64); + } + #[cfg(feature = "only_i32")] + if v > i32::MAX as u64 { + return Ok(Dynamic::from(v)); + } else { + return self.visit_i32(v as i32); + } + } + + #[cfg(not(feature = "no_float"))] + fn visit_f32(self, v: f32) -> Result { + #[cfg(not(feature = "f32_float"))] + return self.visit_f64(v as f64); + #[cfg(feature = "f32_float")] + return Ok(v.into()); + } + #[cfg(not(feature = "no_float"))] + fn visit_f64(self, v: f64) -> Result { + #[cfg(not(feature = "f32_float"))] + return Ok(v.into()); + #[cfg(feature = "f32_float")] + return self.visit_f32(v as f32); + } + + fn visit_char(self, v: char) -> Result { + self.visit_string(v.to_string()) + } + fn visit_str(self, v: &str) -> Result { + Ok(v.into()) + } + fn visit_borrowed_str(self, v: &str) -> Result { + self.visit_str(v) + } + fn visit_string(self, v: String) -> Result { + Ok(v.into()) + } + + fn visit_unit(self) -> Result { + Ok(Dynamic::UNIT) + } + + fn visit_newtype_struct>(self, de: D) -> Result { + Deserialize::deserialize(de) + } + + #[cfg(not(feature = "no_index"))] + fn visit_seq>(self, mut seq: A) -> Result { + let mut arr: Array = Default::default(); + + while let Some(v) = seq.next_element()? { + arr.push(v); + } + + Ok(arr.into()) + } + + #[cfg(not(feature = "no_object"))] + fn visit_map>(self, mut map: M) -> Result { + let mut m: Map = Default::default(); + + while let Some((k, v)) = map.next_entry()? { + m.insert(k, v); + } + + Ok(m.into()) + } +} + +impl<'d> Deserialize<'d> for Dynamic { + fn deserialize>(de: D) -> Result { + de.deserialize_any(DynamicVisitor) + } +} + +impl<'d> Deserialize<'d> for ImmutableString { + fn deserialize>(de: D) -> Result { + let s: String = Deserialize::deserialize(de)?; + Ok(s.into()) + } +} diff --git a/src/serde_impl/mod.rs b/src/serde_impl/mod.rs index d38105a1..1c90abbf 100644 --- a/src/serde_impl/mod.rs +++ b/src/serde_impl/mod.rs @@ -1,7 +1,9 @@ //! Helper module defining serialization/deserialization support for [`serde`]. pub mod de; +mod deserialize; pub mod ser; +mod serialize; mod str; #[cfg(feature = "metadata")] diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index dc62d3c7..edc272cb 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -4,9 +4,8 @@ use crate::stdlib::{boxed::Box, fmt, string::ToString}; use crate::{Dynamic, EvalAltResult, Position}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, - Serializer, }; -use serde::Serialize; +use serde::{Serialize, Serializer}; #[cfg(not(feature = "no_index"))] use crate::Array; diff --git a/src/serde_impl/serialize.rs b/src/serde_impl/serialize.rs new file mode 100644 index 00000000..bdab788f --- /dev/null +++ b/src/serde_impl/serialize.rs @@ -0,0 +1,55 @@ +//! Implementations of [`serde::Serialize`]. + +use crate::dynamic::Union; +use crate::stdlib::string::ToString; +use crate::{Dynamic, ImmutableString}; +use serde::ser::{Serialize, SerializeMap, Serializer}; + +impl Serialize for Dynamic { + fn serialize(&self, ser: S) -> Result { + match &self.0 { + Union::Unit(_, _) => ser.serialize_unit(), + Union::Bool(x, _) => ser.serialize_bool(*x), + Union::Str(s, _) => ser.serialize_str(s.as_str()), + Union::Char(c, _) => ser.serialize_str(&c.to_string()), + #[cfg(not(feature = "only_i32"))] + Union::Int(x, _) => ser.serialize_i64(*x), + #[cfg(feature = "only_i32")] + Union::Int(x, _) => ser.serialize_i32(*x), + #[cfg(not(feature = "no_float"))] + #[cfg(not(feature = "f32_float"))] + Union::Float(x, _) => ser.serialize_f64(**x), + #[cfg(not(feature = "no_float"))] + #[cfg(feature = "f32_float")] + Union::Float(x, _) => ser.serialize_f32(*x), + #[cfg(not(feature = "no_index"))] + Union::Array(a, _) => (**a).serialize(ser), + #[cfg(not(feature = "no_object"))] + Union::Map(m, _) => { + let mut map = ser.serialize_map(Some(m.len()))?; + for (k, v) in m.iter() { + map.serialize_entry(k, v)?; + } + map.end() + } + Union::FnPtr(f, _) => ser.serialize_str(f.fn_name()), + #[cfg(not(feature = "no_std"))] + Union::TimeStamp(_, _) => unimplemented!("serialization of timestamp is not supported"), + + Union::Variant(v, _) => ser.serialize_str((***v).type_name()), + + #[cfg(not(feature = "no_closure"))] + #[cfg(not(feature = "sync"))] + Union::Shared(cell, _) => cell.borrow().serialize(ser), + #[cfg(not(feature = "no_closure"))] + #[cfg(feature = "sync")] + Union::Shared(cell, _) => cell.read().unwrap().serialize(ser), + } + } +} + +impl Serialize for ImmutableString { + fn serialize(&self, ser: S) -> Result { + ser.serialize_str(self.as_str()) + } +} diff --git a/tests/serde.rs b/tests/serde.rs index 75291413..1e1a0ad6 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -2,7 +2,7 @@ use rhai::{ serde::{from_dynamic, to_dynamic}, - Dynamic, Engine, EvalAltResult, INT, + Dynamic, Engine, EvalAltResult, ImmutableString, INT, }; use serde::{Deserialize, Serialize}; @@ -290,15 +290,15 @@ fn test_serde_ser_untagged_enum() -> Result<(), Box> { #[test] fn test_serde_de_primary_types() -> Result<(), Box> { - assert_eq!(42_u16, from_dynamic(&Dynamic::from(42_u16))?); - assert_eq!(42 as INT, from_dynamic(&(42 as INT).into())?); - assert_eq!(true, from_dynamic(&true.into())?); - assert_eq!((), from_dynamic(&().into())?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42_u16))?); + assert_eq!(42, from_dynamic::(&(42 as INT).into())?); + assert_eq!(true, from_dynamic::(&true.into())?); + assert_eq!((), from_dynamic::<()>(&().into())?); #[cfg(not(feature = "no_float"))] { - assert_eq!(123.456_f64, from_dynamic(&123.456_f64.into())?); - assert_eq!(123.456_f32, from_dynamic(&Dynamic::from(123.456_f32))?); + assert_eq!(123.456, from_dynamic::(&123.456_f64.into())?); + assert_eq!(123.456, from_dynamic::(&Dynamic::from(123.456_f32))?); } assert_eq!( @@ -311,14 +311,14 @@ fn test_serde_de_primary_types() -> Result<(), Box> { #[test] fn test_serde_de_integer_types() -> Result<(), Box> { - assert_eq!(42_i8, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_i16, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_i32, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_i64, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u8, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u16, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u32, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u64, from_dynamic(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); Ok(()) } @@ -635,3 +635,42 @@ fn test_serde_de_untagged_enum() -> Result<(), Box> { Ok(()) } + +#[test] +#[cfg(feature = "metadata")] +#[cfg(not(feature = "no_object"))] +#[cfg(not(feature = "no_index"))] +fn test_serde_json() -> serde_json::Result<()> { + let s: ImmutableString = "hello".into(); + assert_eq!(serde_json::to_string(&s)?, r#""hello""#); + + let mut map = Map::new(); + map.insert("a".into(), (123 as INT).into()); + + let arr: Array = vec![(1 as INT).into(), (2 as INT).into(), (3 as INT).into()]; + map.insert("b".into(), arr.into()); + map.insert("c".into(), true.into()); + let d: Dynamic = map.into(); + + let json = serde_json::to_string(&d)?; + + assert!(json.contains("\"a\":123")); + assert!(json.contains("\"b\":[1,2,3]")); + assert!(json.contains("\"c\":true")); + + let d2: Dynamic = serde_json::from_str(&json)?; + + assert!(d2.is::()); + + let mut m = d2.cast::(); + + assert_eq!(m["a"].as_int().unwrap(), 123); + assert!(m["c"].as_bool().unwrap()); + + let a = m.remove("b").unwrap().cast::(); + + assert_eq!(a.len(), 3); + assert_eq!(format!("{:?}", a), "[1, 2, 3]"); + + Ok(()) +} From 4a8debb5edee87c9d7a00f35294dd5a3e100b37d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 18 Jan 2021 10:56:42 +0800 Subject: [PATCH 11/12] Fix builds. --- src/ast.rs | 10 +++++----- src/module/mod.rs | 3 +-- src/serde_impl/metadata.rs | 14 +++++++++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 0a69dd17..910352fe 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -7,9 +7,9 @@ use crate::stdlib::{ borrow::Cow, boxed::Box, fmt, - hash::{Hash, Hasher}, + hash::Hash, num::{NonZeroU64, NonZeroUsize}, - ops::{Add, AddAssign, Deref, DerefMut}, + ops::{Add, AddAssign}, string::String, vec, vec::Vec, @@ -1131,7 +1131,7 @@ pub struct FloatWrapper(FLOAT); #[cfg(not(feature = "no_float"))] impl Hash for FloatWrapper { - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { self.0.to_le_bytes().hash(state); } } @@ -1151,7 +1151,7 @@ impl AsMut for FloatWrapper { } #[cfg(not(feature = "no_float"))] -impl Deref for FloatWrapper { +impl crate::stdlib::ops::Deref for FloatWrapper { type Target = FLOAT; fn deref(&self) -> &Self::Target { @@ -1160,7 +1160,7 @@ impl Deref for FloatWrapper { } #[cfg(not(feature = "no_float"))] -impl DerefMut for FloatWrapper { +impl crate::stdlib::ops::DerefMut for FloatWrapper { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/src/module/mod.rs b/src/module/mod.rs index c145e217..64788b74 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1676,9 +1676,8 @@ impl Module { } /// Get an iterator to the functions in the [`Module`]. - #[cfg(not(feature = "no_optimize"))] - #[cfg(not(feature = "no_function"))] #[inline(always)] + #[allow(dead_code)] pub(crate) fn iter_fn(&self) -> impl Iterator { self.functions.values() } diff --git a/src/serde_impl/metadata.rs b/src/serde_impl/metadata.rs index 85a63d87..94fc32c6 100644 --- a/src/serde_impl/metadata.rs +++ b/src/serde_impl/metadata.rs @@ -145,7 +145,14 @@ impl From<&crate::module::FuncInfo> for FnMetadata { .or_else(|| Some("()".to_string())), signature: info.gen_signature(), doc_comments: if info.func.is_script() { - info.func.get_fn_def().comments.clone() + #[cfg(feature = "no_function")] + { + unreachable!() + } + #[cfg(not(feature = "no_function"))] + { + info.func.get_fn_def().comments.clone() + } } else { Default::default() }, @@ -212,7 +219,7 @@ impl Engine { /// 4) Functions in global modules (optional) pub fn gen_fn_metadata_with_ast_to_json( &self, - ast: &AST, + _ast: &AST, include_global: bool, ) -> serde_json::Result { let mut global: ModuleMetadata = Default::default(); @@ -233,7 +240,8 @@ impl Engine { .map(|f| f.into()) .for_each(|info| global.functions.push(info)); - ast.iter_functions() + #[cfg(not(feature = "no_function"))] + _ast.iter_functions() .map(|f| f.into()) .for_each(|info| global.functions.push(info)); From aef18b5473d5ae9fcfd0006d21d2fbf851716036 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 18 Jan 2021 11:35:55 +0800 Subject: [PATCH 12/12] Add wasm-bindgen and stdweb features. --- Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ef8767ae..e433e996 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,10 @@ metadata = [ "serde", "serde_json"] # enables exporting functions metadata to JS # compiling for no-std no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash/compile-time-rng" ] +# compiling for WASM +wasm-bindgen = [ "instant/wasm-bindgen" ] +stdweb = [ "instant/stdweb" ] + [profile.release] lto = "fat" codegen-units = 1 @@ -94,7 +98,7 @@ default_features = false optional = true [target.'cfg(target_arch = "wasm32")'.dependencies] -instant= { version = "0.1", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant +instant= { version = "0.1" } # WASM implementation of std::time::Instant [package.metadata.docs.rs] features = [ "metadata", "internals" ]