From 06a9ca205742ebfa81b4dbcb66875579fbcf90ee Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Aug 2020 17:21:10 +0800 Subject: [PATCH 01/15] Delete benchmark.yml --- .github/workflows/benchmark.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .github/workflows/benchmark.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml deleted file mode 100644 index df310705..00000000 --- a/.github/workflows/benchmark.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Benchmark -on: - push: - branches: - - master - -jobs: - benchmark: - name: Run Rust benchmark - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: rustup toolchain update nightly && rustup default nightly - - name: Run benchmark - run: cargo +nightly bench | tee output.txt - - name: Store benchmark result - uses: rhysd/github-action-benchmark@v1 - with: - name: Rust Benchmark - tool: 'cargo' - output-file-path: output.txt - # Use personal access token instead of GITHUB_TOKEN due to https://github.community/t5/GitHub-Actions/Github-action-not-triggering-gh-pages-upon-push/td-p/26869/highlight/false - github-token: ${{ secrets.RHAI }} - auto-push: true - # Show alert with commit comment on detecting possible performance regression - alert-threshold: '200%' - comment-on-alert: true - fail-on-alert: true - alert-comment-cc-users: '@schungx' From ac90b7f246769f51e6189f75e02e7dceed54897f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Aug 2020 17:24:22 +0800 Subject: [PATCH 02/15] Revert "Delete benchmark.yml" This reverts commit 06a9ca205742ebfa81b4dbcb66875579fbcf90ee. --- .github/workflows/benchmark.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/benchmark.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..df310705 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,29 @@ +name: Benchmark +on: + push: + branches: + - master + +jobs: + benchmark: + name: Run Rust benchmark + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: rustup toolchain update nightly && rustup default nightly + - name: Run benchmark + run: cargo +nightly bench | tee output.txt + - name: Store benchmark result + uses: rhysd/github-action-benchmark@v1 + with: + name: Rust Benchmark + tool: 'cargo' + output-file-path: output.txt + # Use personal access token instead of GITHUB_TOKEN due to https://github.community/t5/GitHub-Actions/Github-action-not-triggering-gh-pages-upon-push/td-p/26869/highlight/false + github-token: ${{ secrets.RHAI }} + auto-push: true + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '200%' + comment-on-alert: true + fail-on-alert: true + alert-comment-cc-users: '@schungx' From b0ab6e95f514e56b17ebf2a6f0e176a30dd5e458 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Aug 2020 18:38:33 +0800 Subject: [PATCH 03/15] Fix currying in map closure calls. --- src/fn_call.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index d56487e2..c7d9050c 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -705,12 +705,25 @@ impl Engine { #[cfg(not(feature = "no_object"))] if let Some(map) = obj.read_lock::() { if let Some(val) = map.get(_fn_name) { - if let Some(f) = val.read_lock::() { + if let Some(fn_ptr) = val.read_lock::() { // Remap the function name - redirected = f.get_fn_name().clone(); + redirected = fn_ptr.get_fn_name().clone(); _fn_name = &redirected; - // Recalculate the hash based on the new function name - _hash = calc_fn_hash(empty(), _fn_name, idx.len(), empty()); + // Add curried arguments + if !fn_ptr.curry().is_empty() { + fn_ptr + .curry() + .iter() + .cloned() + .enumerate() + .for_each(|(i, v)| idx.insert(i, v)); + } + // Recalculate the hash based on the new function name and new arguments + _hash = if native { + 0 + } else { + calc_fn_hash(empty(), _fn_name, idx.len(), empty()) + }; } } }; From 9f302d4ef5dab3cb570cd15d089e6940b9e5702e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Aug 2020 18:39:24 +0800 Subject: [PATCH 04/15] Update docs and tests. --- README.md | 2 +- RELEASES.md | 2 +- doc/src/start/builds/minimal.md | 4 +-- doc/src/start/builds/performance.md | 21 ++++++++--- doc/src/start/examples/scripts.md | 2 +- scripts/oop.rhai | 56 ++++++++++++++--------------- src/any.rs | 2 +- src/fn_register.rs | 9 ++--- 8 files changed, 51 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 7aef538c..338a1309 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Standard features * Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature). * [Function overloading](https://schungx.github.io/rhai/language/overload.html). * [Operator overloading](https://schungx.github.io/rhai/rust/operators.html). -* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html) with additional support for [currying](https://schungx.github.io/rhai/language/fn-curry.html). +* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html) with additional support for [currying](https://schungx.github.io/rhai/language/fn-curry.html) and [closures](https://schungx.github.io/rhai/language/fn-closure.html). * Some support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html). * Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html). * Serialization/deserialization support via [serde](https://crates.io/crates/serde) (requires the `serde` feature). diff --git a/RELEASES.md b/RELEASES.md index 1d2a842b..7e72c56b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,10 +6,10 @@ Version 0.18.0 This version adds: -* Binding the `this` pointer in a function pointer `call`. * Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions. * Currying of function pointers. * Closures - auto-currying of anonymous functions to capture shared variables from the external scope. +* Binding the `this` pointer in a function pointer `call`. * Capturing call scope via `func!(...)` syntax. New features diff --git a/doc/src/start/builds/minimal.md b/doc/src/start/builds/minimal.md index 46b85007..71ed7bd9 100644 --- a/doc/src/start/builds/minimal.md +++ b/doc/src/start/builds/minimal.md @@ -38,8 +38,8 @@ Omitting arrays ([`no_index`]) yields the most code-size savings, followed by fl ([`no_float`]), checked arithmetic/script resource limits ([`unchecked`]) and finally object maps and custom types ([`no_object`]). Where the usage scenario does not call for loading externally-defined modules, use [`no_module`] to save some bytes. -Disable script-defined functions ([`no_function`]) when the feature is not needed. -Both of these have little code size savings. +Disable script-defined functions ([`no_function`]) and possibly closures ([`no_closure`]) when the features are not needed. +Both of these have some code size savings but not much. Use a Raw [`Engine`] diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md index 27747208..a0c65fe5 100644 --- a/doc/src/start/builds/performance.md +++ b/doc/src/start/builds/performance.md @@ -3,11 +3,12 @@ Performance Build {{#include ../../links.md}} +Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`). + + Use Only One Integer Type ------------------------ -Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`). - If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster because fewer functions need to be loaded. @@ -22,8 +23,8 @@ On 64-bit targets this may not gain much, but on some 32-bit targets this improv requiring more CPU cycles to complete. -Minimize Size of [`Dynamic`] ---------------------------- +Minimize Size of `Dynamic` +------------------------- Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`. @@ -42,3 +43,15 @@ It is cheap to clone, but expensive to modify (a new copy of the string must be Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`) for the best performance with Rhai. + + +Disable Closures +---------------- + +Support for [closures], including capturing shared values, adds material overhead to script evaluation. + +This is because every data access must be checked whether it is a shared value, and if so, take a read +or write lock before reading it. + +Use [`no_closure`] to disable closure and capturing support and optimize the hot path +because there is no need to take locks for shared data. diff --git a/doc/src/start/examples/scripts.md b/doc/src/start/examples/scripts.md index 57c38d13..fff32275 100644 --- a/doc/src/start/examples/scripts.md +++ b/doc/src/start/examples/scripts.md @@ -20,7 +20,7 @@ There are also a number of examples scripts that showcase Rhai's features, all i | [`function_decl3.rhai`]({{repoTree}}/scripts/function_decl3.rhai) | A [function] with many parameters | | [`if1.rhai`]({{repoTree}}/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example | | [`loop.rhai`]({{repoTree}}/scripts/loop.rhai) | Count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop | -| [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] | +| [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] with [closures] | | [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | Just simple addition | | [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | Simple addition and multiplication | | [`op3.rhai`]({{repoTree}}/scripts/op3.rhai) | Change evaluation order with parenthesis | diff --git a/scripts/oop.rhai b/scripts/oop.rhai index fe03b636..e90d690b 100644 --- a/scripts/oop.rhai +++ b/scripts/oop.rhai @@ -1,45 +1,41 @@ -// This script simulates object-oriented programming (OOP) techniques -// using function pointers (Fn) and object maps. +// This script simulates object-oriented programming (OOP) techniques using closures. + +// External variable that will be captured. +let last_value = (); // Define object let obj1 = #{ - _data: 42, // data field - get_data: Fn("getData"), // property getter - action: Fn("action"), // method - update: Fn("update1") // property setter + _data: 42, // data field + get_data: || this._data, // property getter + action: || print("Data=" + this._data), // method + update: |x| { // property setter + this._data = x; + last_value = this._data; // capture 'last_value' + this.action(); + } }; -fn getData() { - this._data -} -fn action() { - print("Data=" + this._data); -} -fn update1(x) { - this._data = x; - this.action(); -} - -if obj1.get_data() > 0 { // property access - obj1.update(123); // call method +if obj1.get_data() > 0 { // property access + obj1.update(123); // call method } else { print("we have a problem here"); } // Define another object based on the first object let obj2 = #{ - _data: 0, // data field - new value - update: Fn("update2") // property setter - another function + _data: 0, // data field - new value + update: |x| { // property setter - another function + this._data = x * 2; + last_value = this._data; // capture 'last_value' + this.action(); + } }; -obj2.fill_with(obj1); // add all other fields from obj1 +obj2.fill_with(obj1); // add all other fields from obj1 -fn update2(x) { - this._data = x * 2; - this.action(); -} - -if obj2.get_data() > 0 { // property access - obj2.update(0); // call method +if obj2.get_data() > 0 { // property access + print("we have another problem here"); } else { - obj2.update(42); // call method + obj2.update(42); // call method } + +print("Should be 84: " + last_value); diff --git a/src/any.rs b/src/any.rs index 5efcad16..aa218127 100644 --- a/src/any.rs +++ b/src/any.rs @@ -600,6 +600,7 @@ impl Dynamic { /// # Panics /// /// Panics under the `no_closure` feature. + #[inline(always)] pub fn into_shared(self) -> Self { #[cfg(not(feature = "no_closure"))] return match self.0 { @@ -833,7 +834,6 @@ impl Dynamic { let data = cell.read().unwrap(); let type_id = (*data).type_id(); - println!("Type = {}", (*data).type_name()); if type_id != TypeId::of::() && TypeId::of::() != TypeId::of::() { None diff --git a/src/fn_register.rs b/src/fn_register.rs index e01f44a1..5c519d4c 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] -use crate::any::{Dynamic, Variant, DynamicWriteLock}; +use crate::any::{Dynamic, DynamicWriteLock, Variant}; use crate::engine::Engine; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::module::Module; @@ -11,12 +11,7 @@ use crate::r#unsafe::unsafe_cast_box; use crate::result::EvalAltResult; use crate::utils::ImmutableString; -use crate::stdlib::{ - any::TypeId, - boxed::Box, - mem, - string::String, -}; +use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String}; /// Trait to register custom functions with the `Engine`. pub trait RegisterFn { From 8a0d0e3e201936a354c59897ff51e02f4cbb9cba Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 10:00:20 +0800 Subject: [PATCH 05/15] Unbox error return for Engine::register_custom_syntax. --- RELEASES.md | 1 + src/syntax.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 7e72c56b..23973fe7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -36,6 +36,7 @@ Breaking changes * `PackagesCollection::get_fn`, `PackagesCollection::contains_fn`, `Module::get_fn` and `Module::contains_fn` now take an additional `public_only` parameter indicating whether only public functions are accepted. * The iterator returned by `Scope::iter` now contains a clone of the `Dynamic` value (unshared). * `Engine::load_package` takes any type that is `Into`. +* Error in `Engine::register_custom_syntax` is no longer `Box`-ed. Housekeeping ------------ diff --git a/src/syntax.rs b/src/syntax.rs index 9dfc255f..4cc937e6 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -100,7 +100,7 @@ impl Engine { ) -> Result> + SendSync + 'static, - ) -> Result<&mut Self, Box> { + ) -> Result<&mut Self, ParseError> { let mut segments: StaticVec<_> = Default::default(); for s in keywords { From 07af296ab0125383587abfff028d1c291795785e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 17:02:11 +0800 Subject: [PATCH 06/15] Fix custom syntax test. --- tests/syntax.rs | 71 ++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/tests/syntax.rs b/tests/syntax.rs index b5a8d7d9..a0cfa5b1 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -1,6 +1,4 @@ -use rhai::{ - Engine, EvalAltResult, EvalContext, Expression, ParseError, ParseErrorType, Scope, INT, -}; +use rhai::{Engine, EvalAltResult, EvalContext, Expression, ParseErrorType, Scope, INT}; #[test] fn test_custom_syntax() -> Result<(), Box> { @@ -19,43 +17,35 @@ fn test_custom_syntax() -> Result<(), Box> { ParseErrorType::Reserved(err) if err == "while" )); - engine - .register_custom_syntax( - &[ - "do", "|", "$ident$", "|", "->", "$block$", "while", "$expr$", - ], - 1, - |engine: &Engine, - context: &mut EvalContext, - scope: &mut Scope, - inputs: &[Expression]| { - let var_name = inputs[0].get_variable_name().unwrap().to_string(); - let stmt = inputs.get(1).unwrap(); - let expr = inputs.get(2).unwrap(); + engine.register_custom_syntax( + &[ + "do", "|", "$ident$", "|", "->", "$block$", "while", "$expr$", + ], + 1, + |engine: &Engine, context: &mut EvalContext, scope: &mut Scope, inputs: &[Expression]| { + let var_name = inputs[0].get_variable_name().unwrap().to_string(); + let stmt = inputs.get(1).unwrap(); + let expr = inputs.get(2).unwrap(); - scope.push(var_name, 0 as INT); + scope.push(var_name, 0 as INT); - loop { - engine.eval_expression_tree(context, scope, stmt)?; + loop { + engine.eval_expression_tree(context, scope, stmt)?; - if !engine - .eval_expression_tree(context, scope, expr)? - .as_bool() - .map_err(|_| { - EvalAltResult::ErrorBooleanArgMismatch( - "do-while".into(), - expr.position(), - ) - })? - { - break; - } + if !engine + .eval_expression_tree(context, scope, expr)? + .as_bool() + .map_err(|_| { + EvalAltResult::ErrorBooleanArgMismatch("do-while".into(), expr.position()) + })? + { + break; } + } - Ok(().into()) - }, - ) - .unwrap(); + Ok(().into()) + }, + )?; // 'while' is now a custom keyword so this it can no longer be a variable engine.consume("let while = 0").expect_err("should error"); @@ -71,10 +61,13 @@ fn test_custom_syntax() -> Result<(), Box> { ); // The first symbol must be an identifier - assert!(matches!( - *engine.register_custom_syntax(&["!"], 0, |_, _, _, _| Ok(().into())).expect_err("should error"), - ParseError(err, _) if *err == ParseErrorType::BadInput("Improper symbol for custom syntax: '!'".to_string()) - )); + assert_eq!( + *engine + .register_custom_syntax(&["!"], 0, |_, _, _, _| Ok(().into())) + .expect_err("should error") + .0, + ParseErrorType::BadInput("Improper symbol for custom syntax: '!'".to_string()) + ); Ok(()) } From f791c64f9631c63530c46ff98ea068564b14022f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 17:02:30 +0800 Subject: [PATCH 07/15] Remove plugins feature. --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11216cc3..db8a63d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,6 @@ jobs: flags: - "" - "--features serde" - - "--features plugins" - "--features unchecked" - "--features sync" - "--features no_optimize" From 5ea08d2b506753a6eeb59350a44cbf958a867b5a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 17:55:36 +0800 Subject: [PATCH 08/15] Bump version. --- Cargo.toml | 2 +- RELEASES.md | 4 ++++ doc/src/context.json | 2 +- src/fn_call.rs | 10 +++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6ffdea4..16066b34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai" -version = "0.18.0" +version = "0.19.0" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index 23973fe7..4b6b9f6d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,10 @@ Rhai Release Notes ================== +Version 0.19.0 +============== + + Version 0.18.0 ============== diff --git a/doc/src/context.json b/doc/src/context.json index 16f803db..792990b1 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,5 @@ { - "version": "0.18.0", + "version": "0.19.0", "repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", diff --git a/src/fn_call.rs b/src/fn_call.rs index c7d9050c..a2377cee 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -694,12 +694,12 @@ impl Engine { && _fn_name == KEYWORD_IS_SHARED && idx.is_empty() { - // take call + // is_shared call Ok((target.is_shared().into(), false)) } else { #[cfg(not(feature = "no_object"))] let redirected; - let mut _hash = hash_script; + let mut hash = hash_script; // Check if it is a map method call in OOP style #[cfg(not(feature = "no_object"))] @@ -719,7 +719,7 @@ impl Engine { .for_each(|(i, v)| idx.insert(i, v)); } // Recalculate the hash based on the new function name and new arguments - _hash = if native { + hash = if native { 0 } else { calc_fn_hash(empty(), _fn_name, idx.len(), empty()) @@ -729,7 +729,7 @@ impl Engine { }; if native { - _hash = 0; + hash = 0; } // Attached object pointer in front of the arguments @@ -737,7 +737,7 @@ impl Engine { let args = arg_values.as_mut(); self.exec_fn_call( - state, lib, _fn_name, _hash, args, is_ref, true, pub_only, None, def_val, level, + state, lib, _fn_name, hash, args, is_ref, true, pub_only, None, def_val, level, ) }?; From ae11dbbefe003ff614ef6aef7049b9f15c9ce8e6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 22:53:01 +0800 Subject: [PATCH 09/15] Fix feature flags. --- .github/workflows/build.yml | 1 - Cargo.toml | 3 +-- RELEASES.md | 6 +----- doc/src/context.json | 2 +- src/any.rs | 4 ++-- src/api.rs | 15 +++++++++++---- src/fn_call.rs | 13 ++++++++----- src/fn_native.rs | 35 ++++++++++++++++++++++------------- src/module.rs | 6 ++---- src/optimize.rs | 7 +++---- src/parser.rs | 7 +++---- src/settings.rs | 4 +++- src/token.rs | 2 ++ tests/closures.rs | 1 + 14 files changed, 60 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db8a63d9..ee08bdeb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,6 @@ on: push: branches: - master - - closures pull_request: {} jobs: diff --git a/Cargo.toml b/Cargo.toml index 16066b34..ea523cb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai" -version = "0.19.0" +version = "0.18.1" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] description = "Embedded scripting for Rust" @@ -23,7 +23,6 @@ smallvec = { version = "1.4.1", default-features = false } [features] #default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"] default = [] -plugins = [] # custom plugins support unchecked = [] # unchecked arithmetic sync = [] # restrict to only types that implement Send + Sync no_optimize = [] # no script optimizer diff --git a/RELEASES.md b/RELEASES.md index 4b6b9f6d..fc8f6d28 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,11 +1,7 @@ Rhai Release Notes ================== -Version 0.19.0 -============== - - -Version 0.18.0 +Version 0.18.1 ============== This version adds: diff --git a/doc/src/context.json b/doc/src/context.json index 792990b1..fb6e4afd 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,5 @@ { - "version": "0.19.0", + "version": "0.18.1", "repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", diff --git a/src/any.rs b/src/any.rs index aa218127..318b97d1 100644 --- a/src/any.rs +++ b/src/any.rs @@ -410,7 +410,7 @@ impl fmt::Display for Dynamic { } #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Union::Shared(cell) => fmt::Display::fmt(*cell.read_lock().unwrap(), f), + Union::Shared(cell) => fmt::Display::fmt(&*cell.read().unwrap(), f), } } } @@ -448,7 +448,7 @@ impl fmt::Debug for Dynamic { } #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Union::Shared(cell) => fmt::Display::fmt(*cell.read_lock().unwrap(), f), + Union::Shared(cell) => fmt::Debug::fmt(&*cell.read().unwrap(), f), } } } diff --git a/src/api.rs b/src/api.rs index 43aaf6d5..cdb4a801 100644 --- a/src/api.rs +++ b/src/api.rs @@ -3,10 +3,9 @@ use crate::any::{Dynamic, Variant}; use crate::engine::{Engine, Imports, State}; use crate::error::ParseError; -use crate::fn_call::ensure_no_data_race; use crate::fn_native::{IteratorFn, SendSync}; use crate::module::{FuncReturn, Module}; -use crate::optimize::{optimize_into_ast, OptimizationLevel}; +use crate::optimize::OptimizationLevel; use crate::parser::AST; use crate::result::EvalAltResult; use crate::scope::Scope; @@ -24,14 +23,22 @@ use crate::{ }; #[cfg(not(feature = "no_function"))] -use crate::{engine::get_script_function_by_signature, fn_args::FuncArgs, utils::StaticVec}; +use crate::{ + engine::get_script_function_by_signature, fn_args::FuncArgs, fn_call::ensure_no_data_race, + utils::StaticVec, +}; + +#[cfg(not(feature = "no_optimize"))] +use crate::optimize::optimize_into_ast; use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, - mem, }; +#[cfg(not(feature = "no_optimize"))] +use crate::stdlib::mem; + #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf}; diff --git a/src/fn_call.rs b/src/fn_call.rs index a2377cee..d93ba533 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -34,6 +34,7 @@ use crate::engine::{FN_IDX_GET, FN_IDX_SET}; use crate::engine::{Map, Target, FN_GET, FN_SET}; #[cfg(not(feature = "no_closure"))] +#[cfg(not(feature = "no_function"))] use crate::scope::Entry as ScopeEntry; use crate::stdlib::{ @@ -48,6 +49,7 @@ use crate::stdlib::{ }; #[cfg(not(feature = "no_closure"))] +#[cfg(not(feature = "no_function"))] use crate::stdlib::{collections::HashSet, string::String}; /// Extract the property name from a getter function name. @@ -140,6 +142,7 @@ impl Drop for ArgBackup<'_> { // Add captured variables into scope #[cfg(not(feature = "no_closure"))] +#[cfg(not(feature = "no_function"))] fn add_captured_variables_into_scope<'s>( externals: &HashSet, captured: Scope<'s>, @@ -449,11 +452,11 @@ impl Engine { hash_script: u64, args: &mut FnCallArgs, is_ref: bool, - is_method: bool, + _is_method: bool, pub_only: bool, _capture: Option, def_val: Option, - level: usize, + _level: usize, ) -> Result<(Dynamic, bool), Box> { // Check for data race. if cfg!(not(feature = "no_closure")) { @@ -510,7 +513,7 @@ impl Engine { add_captured_variables_into_scope(&func.externals, captured, scope); } - let result = if is_method { + let result = if _is_method { // Method call of script function - map first argument to `this` let (first, rest) = args.split_at_mut(1); self.call_script_fn( @@ -522,7 +525,7 @@ impl Engine { fn_name, func, rest, - level, + _level, )? } else { // Normal call of script function - map first argument to `this` @@ -531,7 +534,7 @@ impl Engine { backup.change_first_arg_to_copy(is_ref, args); let result = self.call_script_fn( - scope, mods, state, lib, &mut None, fn_name, func, args, level, + scope, mods, state, lib, &mut None, fn_name, func, args, _level, ); // Restore the original reference diff --git a/src/fn_native.rs b/src/fn_native.rs index a972793b..149e720e 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,21 +1,20 @@ //! Module defining interfaces to native-Rust functions. use crate::any::Dynamic; -use crate::calc_fn_hash; use crate::engine::Engine; use crate::module::Module; -use crate::parser::FnAccess; +use crate::parser::{FnAccess, ScriptFnDef}; use crate::result::EvalAltResult; use crate::token::{is_valid_identifier, Position}; use crate::utils::ImmutableString; #[cfg(not(feature = "no_function"))] -use crate::{module::FuncReturn, parser::ScriptFnDef, utils::StaticVec}; +use crate::{calc_fn_hash, module::FuncReturn, utils::StaticVec}; -use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, string::String, vec::Vec}; +use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec}; #[cfg(not(feature = "no_function"))] -use crate::stdlib::mem; +use crate::stdlib::{iter::empty, mem}; #[cfg(not(feature = "sync"))] use crate::stdlib::rc::Rc; @@ -293,10 +292,11 @@ impl CallableFunction { } } /// Is this a Rhai-scripted function? - #[cfg(not(feature = "no_function"))] pub fn is_script(&self) -> bool { match self { + #[cfg(not(feature = "no_function"))] Self::Script(_) => true, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, } } @@ -314,6 +314,8 @@ impl CallableFunction { pub fn access(&self) -> FnAccess { match self { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, + + #[cfg(not(feature = "no_function"))] Self::Script(f) => f.access, } } @@ -348,10 +350,11 @@ impl CallableFunction { /// # Panics /// /// Panics if the `CallableFunction` is not `Script`. - #[cfg(not(feature = "no_function"))] pub fn get_fn_def(&self) -> &ScriptFnDef { match self { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(), + + #[cfg(not(feature = "no_function"))] Self::Script(f) => f, } } @@ -385,16 +388,22 @@ impl From for CallableFunction { } } -#[cfg(not(feature = "no_function"))] impl From for CallableFunction { - fn from(func: ScriptFnDef) -> Self { - Self::Script(func.into()) + fn from(_func: ScriptFnDef) -> Self { + #[cfg(feature = "no_function")] + unreachable!(); + + #[cfg(not(feature = "no_function"))] + Self::Script(_func.into()) } } -#[cfg(not(feature = "no_function"))] impl From> for CallableFunction { - fn from(func: Shared) -> Self { - Self::Script(func) + fn from(_func: Shared) -> Self { + #[cfg(feature = "no_function")] + unreachable!(); + + #[cfg(not(feature = "no_function"))] + Self::Script(_func) } } diff --git a/src/module.rs b/src/module.rs index fa23abd6..57fe8eb5 100644 --- a/src/module.rs +++ b/src/module.rs @@ -4,13 +4,13 @@ use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; use crate::engine::Engine; use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync}; -use crate::parser::{FnAccess, FnAccess::Public}; +use crate::parser::{FnAccess, FnAccess::Public, ScriptFnDef}; use crate::result::EvalAltResult; use crate::token::{Position, Token}; use crate::utils::{StaticVec, StraightHasherBuilder}; #[cfg(not(feature = "no_function"))] -use crate::{fn_native::Shared, parser::ScriptFnDef}; +use crate::fn_native::Shared; #[cfg(not(feature = "no_module"))] use crate::{ @@ -258,7 +258,6 @@ impl Module { /// Set a script-defined function into the module. /// /// If there is an existing function of the same name and number of arguments, it is replaced. - #[cfg(not(feature = "no_function"))] pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> &mut Self { // None + function name + number of arguments. let hash_script = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty()); @@ -1003,7 +1002,6 @@ impl Module { } /// Get an iterator to the functions in the module. - #[cfg(not(feature = "no_function"))] pub(crate) fn iter_fn( &self, ) -> impl Iterator, Func)> { diff --git a/src/optimize.rs b/src/optimize.rs index 7503c592..ab4147c3 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -7,13 +7,10 @@ use crate::engine::{ }; use crate::fn_native::FnPtr; use crate::module::Module; -use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST}; +use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::utils::StaticVec; -#[cfg(not(feature = "no_function"))] -use crate::parser::ReturnType; - #[cfg(feature = "internals")] use crate::parser::CustomExpr; @@ -46,10 +43,12 @@ impl OptimizationLevel { self == Self::None } /// Is the `OptimizationLevel` Simple. + #[cfg(not(feature = "no_optimize"))] pub fn is_simple(self) -> bool { self == Self::Simple } /// Is the `OptimizationLevel` Full. + #[cfg(not(feature = "no_optimize"))] pub fn is_full(self) -> bool { self == Self::Full } diff --git a/src/parser.rs b/src/parser.rs index 272215ac..c66fa56b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,9 +2,7 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; -use crate::engine::{ - Engine, KEYWORD_FN_PTR_CURRY, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT, -}; +use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::error::{LexError, ParseError, ParseErrorType}; use crate::fn_native::{FnPtr, Shared}; use crate::module::{Module, ModuleRef}; @@ -15,7 +13,7 @@ use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, To use crate::utils::{StaticVec, StraightHasherBuilder}; #[cfg(not(feature = "no_function"))] -use crate::engine::FN_ANONYMOUS; +use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY}; #[cfg(not(feature = "no_object"))] use crate::engine::{make_getter, make_setter}; @@ -3135,6 +3133,7 @@ fn parse_fn( } /// Creates a curried expression from a list of external variables +#[cfg(not(feature = "no_function"))] fn make_curry_from_externals( fn_expr: Expr, externals: StaticVec<(String, Position)>, diff --git a/src/settings.rs b/src/settings.rs index d4949a41..6e9e020d 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,13 +1,15 @@ //! Configuration settings for `Engine`. use crate::engine::Engine; -use crate::optimize::OptimizationLevel; use crate::packages::PackageLibrary; use crate::token::{is_valid_identifier, Token}; #[cfg(not(feature = "no_module"))] use crate::module::ModuleResolver; +#[cfg(not(feature = "no_optimize"))] +use crate::optimize::OptimizationLevel; + use crate::stdlib::{format, string::String}; #[cfg(not(feature = "no_module"))] diff --git a/src/token.rs b/src/token.rs index 4a4d5fc1..c1968d23 100644 --- a/src/token.rs +++ b/src/token.rs @@ -680,6 +680,7 @@ impl Token { } /// Convert a token into a function name, if possible. + #[cfg(not(feature = "no_function"))] pub(crate) fn into_function_name_for_override(self) -> Result { match self { Self::Reserved(s) if can_override_keyword(&s) => Ok(s), @@ -1442,6 +1443,7 @@ pub fn is_keyword_function(name: &str) -> bool { } /// Can this keyword be overridden as a function? +#[cfg(not(feature = "no_function"))] #[inline(always)] pub fn can_override_keyword(name: &str) -> bool { match name { diff --git a/tests/closures.rs b/tests/closures.rs index 0736654b..3fc1cbd5 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -83,6 +83,7 @@ fn test_closures() -> Result<(), Box> { #[test] #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_object"))] +#[cfg(not(feature = "sync"))] fn test_closures_data_race() -> Result<(), Box> { let engine = Engine::new(); From bb2c9f7c5933bff181ea21f28c4fc91578c58434 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 23:11:54 +0800 Subject: [PATCH 10/15] Bump version. --- Cargo.toml | 2 +- RELEASES.md | 6 +++++- doc/src/context.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea523cb9..73cc5b2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai" -version = "0.18.1" +version = "0.19.0" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index fc8f6d28..87cb5bdb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,10 @@ Rhai Release Notes ================== +Version 0.19.0 +============== + + Version 0.18.1 ============== @@ -8,7 +12,7 @@ This version adds: * Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions. * Currying of function pointers. -* Closures - auto-currying of anonymous functions to capture shared variables from the external scope. +* Closures - auto-currying of anonymous functions to capture shared variables from the external scope. Use the `no_closure` feature to disable sharing values and capturing. * Binding the `this` pointer in a function pointer `call`. * Capturing call scope via `func!(...)` syntax. diff --git a/doc/src/context.json b/doc/src/context.json index fb6e4afd..792990b1 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,5 @@ { - "version": "0.18.1", + "version": "0.19.0", "repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", From ffe52bf437c8f9a658a99d0acf20de2545afa5cc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Aug 2020 23:23:53 +0800 Subject: [PATCH 11/15] Allow omitting the alias in import statement. --- doc/src/language/modules/import.md | 11 +++++++++-- src/engine.rs | 9 ++++++--- src/parser.rs | 14 ++++---------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/doc/src/language/modules/import.md b/doc/src/language/modules/import.md index 5e0a377a..b592ff74 100644 --- a/doc/src/language/modules/import.md +++ b/doc/src/language/modules/import.md @@ -7,10 +7,17 @@ Import a Module `import` Statement ----------------- -A module can be _imported_ via the `import` statement, and its members are accessed via '`::`' similar to C++. +A module can be _imported_ via the `import` statement, and be given a name. +Its members can be accessed via '`::`' similar to C++. + +A module that is only `import`-ed but not under any module name is commonly used for initialization purposes, +where the module script contains initialization statements that puts the functions registered with the +[`Engine`] into a particular state. ```rust -import "crypto" as lock; // import the script file 'crypto.rhai' as a module named 'lock' +import "crypto_init"; // run the script file 'crypto_init.rhai' without creating an imported module + +import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock' lock::encrypt(secret); // use functions defined under the module via '::' diff --git a/src/engine.rs b/src/engine.rs index 65228a8f..d485f22b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1806,7 +1806,7 @@ impl Engine { // Import statement #[cfg(not(feature = "no_module"))] Stmt::Import(x) => { - let (expr, (name, _pos), _) = x.as_ref(); + let (expr, alias, _pos) = x.as_ref(); // Guard against too many modules #[cfg(not(feature = "unchecked"))] @@ -1820,8 +1820,11 @@ impl Engine { { if let Some(resolver) = &self.module_resolver { let mut module = resolver.resolve(self, &path, expr.position())?; - module.index_all_sub_modules(); - mods.push((name.clone().into(), module)); + + if let Some((name, _)) = alias { + module.index_all_sub_modules(); + mods.push((name.clone().into(), module)); + } state.modules += 1; diff --git a/src/parser.rs b/src/parser.rs index c66fa56b..8b3bf3ea 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -568,7 +568,7 @@ pub enum Stmt { ReturnWithVal(Box<((ReturnType, Position), Option, Position)>), /// import expr as module #[cfg(not(feature = "no_module"))] - Import(Box<(Expr, (String, Position), Position)>), + Import(Box<(Expr, Option<(String, Position)>, Position)>), /// expr id as name, ... #[cfg(not(feature = "no_module"))] Export( @@ -2685,14 +2685,8 @@ fn parse_import( let expr = parse_expr(input, state, lib, settings.level_up())?; // import expr as ... - match input.next().unwrap() { - (Token::As, _) => (), - (_, pos) => { - return Err( - PERR::MissingToken(Token::As.into(), "in this import statement".into()) - .into_err(pos), - ) - } + if !match_token(input, Token::As)? { + return Ok(Stmt::Import(Box::new((expr, None, token_pos)))); } // import expr as name ... @@ -2709,7 +2703,7 @@ fn parse_import( Ok(Stmt::Import(Box::new(( expr, - (name, settings.pos), + Some((name, settings.pos)), token_pos, )))) } From fff08d29b6e6e8ee6fa020157e61699b1e889d65 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Aug 2020 00:01:16 +0800 Subject: [PATCH 12/15] Fix CI. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee08bdeb..f4a476a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all {{matrix.flags}} + args: --all ${{matrix.flags}} # no-std builds are a bit more extensive to test no_std_build: name: NoStdBuild From 41b41ff834e114778689207aac9224a014eca91c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Aug 2020 00:24:25 +0800 Subject: [PATCH 13/15] Fix tests. --- tests/side_effects.rs | 8 ++++---- tests/stack.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/side_effects.rs b/tests/side_effects.rs index bee1cb2f..69e33de1 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -5,16 +5,16 @@ use std::sync::{Arc, Mutex, RwLock}; /// Simulate a command object. struct Command { /// Simulate an external state. - state: i64, + state: INT, } impl Command { /// Do some action. - pub fn action(&mut self, val: i64) { + pub fn action(&mut self, val: INT) { self.state = val; } /// Get current value. - pub fn get(&self) -> i64 { + pub fn get(&self) -> INT { self.state } } @@ -39,7 +39,7 @@ fn test_side_effects_command() -> Result<(), Box> { // Register type. engine.register_type_with_name::("CommandType"); - engine.register_fn("action", |api: &mut API, x: i64| { + engine.register_fn("action", |api: &mut API, x: INT| { let mut command = api.lock().unwrap(); let val = command.get(); command.action(val + x); diff --git a/tests/stack.rs b/tests/stack.rs index 29828fef..47202b47 100644 --- a/tests/stack.rs +++ b/tests/stack.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "unchecked"))] -use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType}; +use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType, INT}; #[test] #[cfg(not(feature = "no_function"))] @@ -7,7 +7,7 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box> { let engine = Engine::new(); assert_eq!( - engine.eval::( + engine.eval::( r" fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } } foo(8) From 2f395492de5a9ce4c253ec227cdfc02f7ad83108 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Aug 2020 10:17:32 +0800 Subject: [PATCH 14/15] Simply error code. --- doc/src/engine/custom-syntax.md | 6 +- doc/src/rust/modules/imp-resolver.md | 2 +- src/api.rs | 31 ++---- src/engine.rs | 148 +++++++++++---------------- src/fn_call.rs | 80 ++++++++------- src/fn_native.rs | 5 +- src/module.rs | 12 +-- src/packages/arithmetic.rs | 93 +++++++++-------- src/packages/array_basic.rs | 5 +- src/packages/math_basic.rs | 10 +- src/packages/string_more.rs | 8 +- src/packages/time_basic.rs | 12 +-- src/result.rs | 6 ++ src/serde/de.rs | 10 +- src/serde/ser.rs | 38 +++---- src/serde/str.rs | 5 +- 16 files changed, 228 insertions(+), 243 deletions(-) diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index 8f308106..8d0820c8 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -200,8 +200,10 @@ fn implementation_func( // Evaluate the condition expression let stop = !engine.eval_expression_tree(context, scope, condition)? - .as_bool().map_err(|_| EvalAltResult::ErrorBooleanArgMismatch( - "do-while".into(), expr.position()))?; + .as_bool() + .map_err(|_| EvalAltResult::ErrorBooleanArgMismatch( + "do-while".into(), expr.position() + ))?; if stop { break; diff --git a/doc/src/rust/modules/imp-resolver.md b/doc/src/rust/modules/imp-resolver.md index 55d68315..d1d9ed38 100644 --- a/doc/src/rust/modules/imp-resolver.md +++ b/doc/src/rust/modules/imp-resolver.md @@ -38,7 +38,7 @@ impl ModuleResolver for MyModuleResolver { let module: Module = load_secret_module(path); Ok(module) } else { - Err(Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos))) + Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) } } } diff --git a/src/api.rs b/src/api.rs index cdb4a801..34af07a1 100644 --- a/src/api.rs +++ b/src/api.rs @@ -608,21 +608,13 @@ impl Engine { #[cfg(not(target_arch = "wasm32"))] fn read_file(path: PathBuf) -> Result> { let mut f = File::open(path.clone()).map_err(|err| { - Box::new(EvalAltResult::ErrorReadingScriptFile( - path.clone(), - Position::none(), - err, - )) + EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err) })?; let mut contents = String::new(); f.read_to_string(&mut contents).map_err(|err| { - Box::new(EvalAltResult::ErrorReadingScriptFile( - path.clone(), - Position::none(), - err, - )) + EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err) })?; Ok(contents) @@ -1042,11 +1034,12 @@ impl Engine { let typ = self.map_type_name(result.type_name()); return result.try_cast::().ok_or_else(|| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::none(), - )) + ) + .into() }); } @@ -1190,11 +1183,12 @@ impl Engine { let typ = self.map_type_name(result.type_name()); return result.try_cast().ok_or_else(|| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::none(), - )) + ) + .into() }); } @@ -1278,13 +1272,8 @@ impl Engine { ) -> FuncReturn { let lib = lib.as_ref(); let mut args: StaticVec<_> = arg_values.iter_mut().collect(); - let fn_def = - get_script_function_by_signature(lib, name, args.len(), true).ok_or_else(|| { - Box::new(EvalAltResult::ErrorFunctionNotFound( - name.into(), - Position::none(), - )) - })?; + let fn_def = get_script_function_by_signature(lib, name, args.len(), true) + .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?; let mut state = State::new(); let mut mods = Imports::new(); diff --git a/src/engine.rs b/src/engine.rs index d485f22b..9a771ac6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -228,9 +228,7 @@ impl Target<'_> { #[cfg(not(feature = "no_object"))] Self::LockGuard((r, _)) => **r = new_val, Self::Value(_) => { - return Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS( - Position::none(), - ))) + return EvalAltResult::ErrorAssignmentToUnknownLHS(Position::none()).into(); } #[cfg(not(feature = "no_index"))] Self::StringChar(string, index, _) if string.is::() => { @@ -517,12 +515,7 @@ pub fn search_imports<'s>( .rev() .find(|(n, _)| n == root) .map(|(_, m)| m) - .ok_or_else(|| { - Box::new(EvalAltResult::ErrorModuleNotFound( - root.to_string(), - *root_pos, - )) - })? + .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *root_pos))? }) } @@ -550,12 +543,7 @@ pub fn search_imports_mut<'s>( .rev() .find(|(n, _)| n == root) .map(|(_, m)| m) - .ok_or_else(|| { - Box::new(EvalAltResult::ErrorModuleNotFound( - root.to_string(), - *root_pos, - )) - })? + .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *root_pos))? }) } @@ -577,10 +565,11 @@ pub fn search_namespace<'s, 'a>( .get_qualified_var_mut(*hash_var) .map_err(|err| match *err { EvalAltResult::ErrorVariableNotFound(_, _) => { - Box::new(EvalAltResult::ErrorVariableNotFound( + EvalAltResult::ErrorVariableNotFound( format!("{}{}", modules, name), *pos, - )) + ) + .into() } _ => err.new_position(*pos), })?; @@ -612,7 +601,7 @@ pub fn search_scope_only<'s, 'a>( if let Some(val) = this_ptr { return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos)); } else { - return Err(Box::new(EvalAltResult::ErrorUnboundThis(*pos))); + return EvalAltResult::ErrorUnboundThis(*pos).into(); } } @@ -625,7 +614,7 @@ pub fn search_scope_only<'s, 'a>( // Find the variable in the scope scope .get_index(name) - .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), *pos)))? + .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.into(), *pos))? .0 }; @@ -634,7 +623,7 @@ pub fn search_scope_only<'s, 'a>( // Check for data race - probably not necessary because the only place it should conflict is in a method call // when the object variable is also used as a parameter. // if cfg!(not(feature = "no_closure")) && val.is_locked() { - // return Err(Box::new(EvalAltResult::ErrorDataRace(name.into(), *pos))); + // return EvalAltResult::ErrorDataRace(name.into(), *pos).into(); // } Ok((val, name, typ, *pos)) @@ -966,10 +955,7 @@ impl Engine { } } // Syntax error - _ => Err(Box::new(EvalAltResult::ErrorDotExpr( - "".into(), - rhs.position(), - ))), + _ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(), } } @@ -1016,10 +1002,8 @@ impl Engine { // Constants cannot be modified match typ { ScopeEntryType::Constant if new_val.is_some() => { - return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( - var_name.to_string(), - pos, - ))); + return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos) + .into(); } ScopeEntryType::Constant | ScopeEntryType::Normal => (), } @@ -1033,9 +1017,7 @@ impl Engine { } // {expr}.??? = ??? or {expr}[???] = ??? expr if new_val.is_some() => { - return Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS( - expr.position(), - ))); + return EvalAltResult::ErrorAssignmentToUnknownLHS(expr.position()).into(); } // {expr}.??? or {expr}[???] expr => { @@ -1160,12 +1142,10 @@ impl Engine { arr.get_mut(index as usize) .map(Target::from) .ok_or_else(|| { - Box::new(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)) + EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into() }) } else { - Err(Box::new(EvalAltResult::ErrorArrayBounds( - arr_len, index, idx_pos, - ))) + EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into() } } @@ -1200,13 +1180,11 @@ impl Engine { if index >= 0 { let offset = index as usize; let ch = s.chars().nth(offset).ok_or_else(|| { - Box::new(EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)) + EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos) })?; Ok(Target::StringChar(val, offset, ch.into())) } else { - Err(Box::new(EvalAltResult::ErrorStringBounds( - chars_len, index, idx_pos, - ))) + EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos).into() } } @@ -1227,10 +1205,11 @@ impl Engine { }) } - _ => Err(Box::new(EvalAltResult::ErrorIndexingType( + _ => EvalAltResult::ErrorIndexingType( self.map_type_name(val.type_name()).into(), Position::none(), - ))), + ) + .into(), } } @@ -1284,15 +1263,15 @@ impl Engine { // Only allows String or char Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(&s).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()), - _ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))), + _ => EvalAltResult::ErrorInExpr(lhs.position()).into(), }, Dynamic(Union::Str(rhs_value)) => match lhs_value { // Only allows String or char Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()), - _ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))), + _ => EvalAltResult::ErrorInExpr(lhs.position()).into(), }, - _ => Err(Box::new(EvalAltResult::ErrorInExpr(rhs.position()))), + _ => EvalAltResult::ErrorInExpr(rhs.position()).into(), } } @@ -1323,7 +1302,7 @@ impl Engine { if let Some(val) = this_ptr { Ok(val.clone()) } else { - Err(Box::new(EvalAltResult::ErrorUnboundThis((x.0).1))) + EvalAltResult::ErrorUnboundThis((x.0).1).into() } } Expr::Variable(_) => { @@ -1454,16 +1433,13 @@ impl Engine { Ok(Default::default()) } // Error assignment to constant - expr if expr.is_constant() => { - Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( - expr.get_constant_str(), - expr.position(), - ))) - } - // Syntax error - expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS( + expr if expr.is_constant() => EvalAltResult::ErrorAssignmentToConstant( + expr.get_constant_str(), expr.position(), - ))), + ) + .into(), + // Syntax error + expr => EvalAltResult::ErrorAssignmentToUnknownLHS(expr.position()).into(), } } @@ -1632,7 +1608,7 @@ impl Engine { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() - .map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(expr.position()))) + .map_err(|_| EvalAltResult::ErrorLogicGuard(expr.position()).into()) .and_then(|guard_val| { if guard_val { self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) @@ -1665,9 +1641,7 @@ impl Engine { } } Ok(false) => return Ok(Default::default()), - Err(_) => { - return Err(Box::new(EvalAltResult::ErrorLogicGuard(expr.position()))) - } + Err(_) => return EvalAltResult::ErrorLogicGuard(expr.position()).into(), } }, @@ -1727,43 +1701,45 @@ impl Engine { state.scope_level -= 1; Ok(Default::default()) } else { - Err(Box::new(EvalAltResult::ErrorFor(x.1.position()))) + EvalAltResult::ErrorFor(x.1.position()).into() } } // Continue statement - Stmt::Continue(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(false, *pos))), + Stmt::Continue(pos) => EvalAltResult::ErrorLoopBreak(false, *pos).into(), // Break statement - Stmt::Break(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(true, *pos))), + Stmt::Break(pos) => EvalAltResult::ErrorLoopBreak(true, *pos).into(), // Return value Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { let expr = x.1.as_ref().unwrap(); - Err(Box::new(EvalAltResult::Return( + EvalAltResult::Return( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, (x.0).1, - ))) + ) + .into() } // Empty return Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Return => { - Err(Box::new(EvalAltResult::Return(Default::default(), (x.0).1))) + EvalAltResult::Return(Default::default(), (x.0).1).into() } // Throw value Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => { let expr = x.1.as_ref().unwrap(); let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; - Err(Box::new(EvalAltResult::ErrorRuntime( + EvalAltResult::ErrorRuntime( val.take_string().unwrap_or_else(|_| "".into()), (x.0).1, - ))) + ) + .into() } // Empty throw Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Exception => { - Err(Box::new(EvalAltResult::ErrorRuntime("".into(), (x.0).1))) + EvalAltResult::ErrorRuntime("".into(), (x.0).1).into() } Stmt::ReturnWithVal(_) => unreachable!(), @@ -1811,7 +1787,7 @@ impl Engine { // Guard against too many modules #[cfg(not(feature = "unchecked"))] if state.modules >= self.limits.max_modules { - return Err(Box::new(EvalAltResult::ErrorTooManyModules(*_pos))); + return EvalAltResult::ErrorTooManyModules(*_pos).into(); } if let Some(path) = self @@ -1830,13 +1806,13 @@ impl Engine { Ok(Default::default()) } else { - Err(Box::new(EvalAltResult::ErrorModuleNotFound( - path.to_string(), - expr.position(), - ))) + Err( + EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position()) + .into(), + ) } } else { - Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) + EvalAltResult::ErrorImportExpr(expr.position()).into() } } @@ -1849,10 +1825,7 @@ impl Engine { let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id); scope.set_entry_alias(index, alias.clone()); } else { - return Err(Box::new(EvalAltResult::ErrorVariableNotFound( - id.into(), - *id_pos, - ))); + return EvalAltResult::ErrorVariableNotFound(id.into(), *id_pos).into(); } } Ok(Default::default()) @@ -1976,26 +1949,29 @@ impl Engine { let (arr, map, s) = calc_size(result.as_ref().unwrap()); if s > self.limits.max_string_size { - Err(Box::new(EvalAltResult::ErrorDataTooLarge( + EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), self.limits.max_string_size, s, Position::none(), - ))) + ) + .into() } else if arr > self.limits.max_array_size { - Err(Box::new(EvalAltResult::ErrorDataTooLarge( + EvalAltResult::ErrorDataTooLarge( "Size of array".to_string(), self.limits.max_array_size, arr, Position::none(), - ))) + ) + .into() } else if map > self.limits.max_map_size { - Err(Box::new(EvalAltResult::ErrorDataTooLarge( + EvalAltResult::ErrorDataTooLarge( "Number of properties in object map".to_string(), self.limits.max_map_size, map, Position::none(), - ))) + ) + .into() } else { result } @@ -2009,16 +1985,14 @@ impl Engine { #[cfg(not(feature = "unchecked"))] // Guard against too many operations if self.limits.max_operations > 0 && state.operations > self.limits.max_operations { - return Err(Box::new(EvalAltResult::ErrorTooManyOperations( - Position::none(), - ))); + return EvalAltResult::ErrorTooManyOperations(Position::none()).into(); } // Report progress - only in steps if let Some(progress) = &self.progress { if !progress(&state.operations) { // Terminate script if progress returns false - return Err(Box::new(EvalAltResult::ErrorTerminated(Position::none()))); + return EvalAltResult::ErrorTerminated(Position::none()).into(); } } diff --git a/src/fn_call.rs b/src/fn_call.rs index d93ba533..20557748 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -178,10 +178,11 @@ pub fn ensure_no_data_race( .enumerate() .find(|(_, a)| a.is_locked()) { - return Err(Box::new(EvalAltResult::ErrorDataRace( + return EvalAltResult::ErrorDataRace( format!("argument #{} of function '{}'", n + 1 + skip, fn_name), Position::none(), - ))); + ) + .into(); } } @@ -237,22 +238,22 @@ impl Engine { return Ok(match fn_name { KEYWORD_PRINT => ( (self.print)(result.as_str().map_err(|typ| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::none(), - )) + ) })?) .into(), false, ), KEYWORD_DEBUG => ( (self.debug)(result.as_str().map_err(|typ| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::none(), - )) + ) })?) .into(), false, @@ -276,56 +277,60 @@ impl Engine { // Getter function not found? if let Some(prop) = extract_prop_from_getter(fn_name) { - return Err(Box::new(EvalAltResult::ErrorDotExpr( + return EvalAltResult::ErrorDotExpr( format!( "Unknown property '{}' for {}, or it is write-only", prop, self.map_type_name(args[0].type_name()) ), Position::none(), - ))); + ) + .into(); } // Setter function not found? if let Some(prop) = extract_prop_from_setter(fn_name) { - return Err(Box::new(EvalAltResult::ErrorDotExpr( + return EvalAltResult::ErrorDotExpr( format!( "Unknown property '{}' for {}, or it is read-only", prop, self.map_type_name(args[0].type_name()) ), Position::none(), - ))); + ) + .into(); } // index getter function not found? #[cfg(not(feature = "no_index"))] if fn_name == FN_IDX_GET && args.len() == 2 { - return Err(Box::new(EvalAltResult::ErrorFunctionNotFound( + return EvalAltResult::ErrorFunctionNotFound( format!( "{} [{}]", self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), ), Position::none(), - ))); + ) + .into(); } // index setter function not found? #[cfg(not(feature = "no_index"))] if fn_name == FN_IDX_SET { - return Err(Box::new(EvalAltResult::ErrorFunctionNotFound( + return EvalAltResult::ErrorFunctionNotFound( format!( "{} [{}]=", self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), ), Position::none(), - ))); + ) + .into(); } // Raise error - Err(Box::new(EvalAltResult::ErrorFunctionNotFound( + EvalAltResult::ErrorFunctionNotFound( format!( "{} ({})", fn_name, @@ -339,7 +344,8 @@ impl Engine { .join(", ") ), Position::none(), - ))) + ) + .into() } /// Call a script-defined function. @@ -400,17 +406,15 @@ impl Engine { // Convert return statement to return value EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::ErrorInFunctionCall(name, err, _) => { - Err(Box::new(EvalAltResult::ErrorInFunctionCall( + EvalAltResult::ErrorInFunctionCall( format!("{} > {}", fn_name, name), err, Position::none(), - ))) + ) + .into() } - _ => Err(Box::new(EvalAltResult::ErrorInFunctionCall( - fn_name.to_string(), - err, - Position::none(), - ))), + _ => EvalAltResult::ErrorInFunctionCall(fn_name.to_string(), err, Position::none()) + .into(), }); // Remove all local variables @@ -482,20 +486,22 @@ impl Engine { KEYWORD_FN_PTR if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) => { - Err(Box::new(EvalAltResult::ErrorRuntime( + EvalAltResult::ErrorRuntime( "'Fn' should not be called in method style. Try Fn(...);".into(), Position::none(), - ))) + ) + .into() } // eval - reaching this point it must be a method-style call KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) => { - Err(Box::new(EvalAltResult::ErrorRuntime( + EvalAltResult::ErrorRuntime( "'eval' should not be called in method style. Try eval(...);".into(), Position::none(), - ))) + ) + .into() } // Normal script function call @@ -783,11 +789,12 @@ impl Engine { return arg_value .take_immutable_string() .map_err(|typ| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), expr.position(), - )) + ) + .into() }) .and_then(|s| FnPtr::try_from(s)) .map(Into::::into) @@ -801,11 +808,12 @@ impl Engine { let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; if !fn_ptr.is::() { - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), self.map_type_name(fn_ptr.type_name()).into(), expr.position(), - ))); + ) + .into(); } let (fn_name, fn_curry) = fn_ptr.cast::().take_data(); @@ -855,11 +863,12 @@ impl Engine { // Recalculate hash hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty()); } else { - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), fn_name.type_name().into(), expr.position(), - ))); + ) + .into(); } } @@ -1048,7 +1057,7 @@ impl Engine { } Some(f) => f.get_native_fn()(self, lib, args.as_mut()), None if def_val.is_some() => Ok(def_val.unwrap().into()), - None => Err(Box::new(EvalAltResult::ErrorFunctionNotFound( + None => EvalAltResult::ErrorFunctionNotFound( format!( "{}{} ({})", modules, @@ -1063,7 +1072,8 @@ impl Engine { .join(", ") ), Position::none(), - ))), + ) + .into(), } } } diff --git a/src/fn_native.rs b/src/fn_native.rs index 149e720e..cc3ed0dc 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -175,10 +175,7 @@ impl TryFrom for FnPtr { if is_valid_identifier(value.chars()) { Ok(Self(value, Default::default())) } else { - Err(Box::new(EvalAltResult::ErrorFunctionNotFound( - value.into(), - Position::none(), - ))) + EvalAltResult::ErrorFunctionNotFound(value.into(), Position::none()).into() } } } diff --git a/src/module.rs b/src/module.rs index 57fe8eb5..b3bd5598 100644 --- a/src/module.rs +++ b/src/module.rs @@ -248,10 +248,7 @@ impl Module { hash_var: u64, ) -> Result<&mut Dynamic, Box> { self.all_variables.get_mut(&hash_var).ok_or_else(|| { - Box::new(EvalAltResult::ErrorVariableNotFound( - String::new(), - Position::none(), - )) + EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into() }) } @@ -1517,7 +1514,7 @@ mod stat { self.0 .get(path) .cloned() - .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos))) + .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) } } } @@ -1597,10 +1594,7 @@ mod collection { } } - Err(Box::new(EvalAltResult::ErrorModuleNotFound( - path.into(), - pos, - ))) + EvalAltResult::ErrorModuleNotFound(path.into(), pos).into() } } } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index d8e256cc..f40f1d11 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -17,7 +17,6 @@ use num_traits::{ use num_traits::float::Float; use crate::stdlib::{ - boxed::Box, fmt::Display, format, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, @@ -26,28 +25,31 @@ use crate::stdlib::{ // Checked add pub fn add(x: T, y: T) -> FuncReturn { x.checked_add(&y).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Addition overflow: {} + {}", x, y), Position::none(), - )) + ) + .into() }) } // Checked subtract pub fn sub(x: T, y: T) -> FuncReturn { x.checked_sub(&y).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Subtraction underflow: {} - {}", x, y), Position::none(), - )) + ) + .into() }) } // Checked multiply pub fn mul(x: T, y: T) -> FuncReturn { x.checked_mul(&y).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Multiplication overflow: {} * {}", x, y), Position::none(), - )) + ) + .into() }) } // Checked divide @@ -57,26 +59,26 @@ where { // Detect division by zero if y == T::zero() { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Division by zero: {} / {}", x, y), Position::none(), - ))); + ) + .into(); } x.checked_div(&y).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Division overflow: {} / {}", x, y), Position::none(), - )) + ) + .into() }) } // Checked negative - e.g. -(i32::MIN) will overflow i32::MAX pub fn neg(x: T) -> FuncReturn { x.checked_neg().ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( - format!("Negation overflow: -{}", x), - Position::none(), - )) + EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) + .into() }) } // Checked absolute @@ -87,10 +89,8 @@ pub fn abs(x: T) -> FuncReturn { Ok(x) } else { x.checked_neg().ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( - format!("Negation overflow: -{}", x), - Position::none(), - )) + EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) + .into() }) } } @@ -140,34 +140,38 @@ fn binary_xor(x: T, y: T) -> FuncReturn<::Output> { pub fn shl(x: T, y: INT) -> FuncReturn { // Cannot shift by a negative number of bits if y < 0 { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Left-shift by a negative number: {} << {}", x, y), Position::none(), - ))); + ) + .into(); } CheckedShl::checked_shl(&x, y as u32).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Left-shift by too many bits: {} << {}", x, y), Position::none(), - )) + ) + .into() }) } // Checked right-shift pub fn shr(x: T, y: INT) -> FuncReturn { // Cannot shift by a negative number of bits if y < 0 { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Right-shift by a negative number: {} >> {}", x, y), Position::none(), - ))); + ) + .into(); } CheckedShr::checked_shr(&x, y as u32).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Right-shift by too many bits: {} % {}", x, y), Position::none(), - )) + ) + .into() }) } // Unchecked left-shift - may panic if shifting by a negative number of bits @@ -181,10 +185,11 @@ pub fn shr_u>(x: T, y: T) -> FuncReturn<>::Output> { // Checked modulo pub fn modulo(x: T, y: T) -> FuncReturn { x.checked_rem(&y).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Modulo division by zero or overflow: {} % {}", x, y), Position::none(), - )) + ) + .into() }) } // Unchecked modulo - may panic if dividing by zero @@ -195,35 +200,40 @@ fn modulo_u(x: T, y: T) -> FuncReturn<::Output> { pub fn pow_i_i(x: INT, y: INT) -> FuncReturn { if cfg!(not(feature = "only_i32")) { if y > (u32::MAX as INT) { - Err(Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Integer raised to too large an index: {} ~ {}", x, y), Position::none(), - ))) + ) + .into() } else if y < 0 { - Err(Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Integer raised to a negative index: {} ~ {}", x, y), Position::none(), - ))) + ) + .into() } else { x.checked_pow(y as u32).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Power overflow: {} ~ {}", x, y), Position::none(), - )) + ) + .into() }) } } else { if y < 0 { - Err(Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Integer raised to a negative index: {} ~ {}", x, y), Position::none(), - ))) + ) + .into() } else { x.checked_pow(y as u32).ok_or_else(|| { - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!("Power overflow: {} ~ {}", x, y), Position::none(), - )) + ) + .into() }) } } @@ -242,10 +252,11 @@ pub fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn { pub fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { // Raise to power that is larger than an i32 if y > (i32::MAX as INT) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Number raised to too large an index: {} ~ {}", x, y), Position::none(), - ))); + ) + .into(); } Ok(x.powi(y as i32)) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 5574a4f5..fc6ca5ec 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -43,12 +43,13 @@ fn pad( && len > 0 && (len as usize) > _engine.limits.max_array_size { - return Err(Box::new(EvalAltResult::ErrorDataTooLarge( + return EvalAltResult::ErrorDataTooLarge( "Size of array".to_string(), _engine.limits.max_array_size, len as usize, Position::none(), - ))); + ) + .into(); } if len > 0 { diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index f8fd2d33..0818e8fe 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -12,7 +12,7 @@ use crate::{result::EvalAltResult, token::Position}; use num_traits::float::Float; #[cfg(not(feature = "no_float"))] -use crate::stdlib::{boxed::Box, format}; +use crate::stdlib::format; #[allow(dead_code)] #[cfg(feature = "only_i32")] @@ -109,10 +109,10 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { "to_int", |x: f32| { if x > (MAX_INT as f32) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Integer overflow: to_int({})", x), Position::none(), - ))); + ).into(); } Ok(x.trunc() as INT) @@ -122,10 +122,10 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { "to_int", |x: FLOAT| { if x > (MAX_INT as FLOAT) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Integer overflow: to_int({})", x), Position::none(), - ))); + ).into(); } Ok(x.trunc() as INT) diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 149823c4..0b58c19d 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -231,12 +231,12 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] if _engine.limits.max_string_size > 0 && len > 0 && (len as usize) > _engine.limits.max_string_size { - return Err(Box::new(EvalAltResult::ErrorDataTooLarge( + return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), _engine.limits.max_string_size, len as usize, Position::none(), - ))); + ).into(); } if len > 0 { @@ -254,12 +254,12 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str #[cfg(not(feature = "unchecked"))] if _engine.limits.max_string_size > 0 && s.len() > _engine.limits.max_string_size { - return Err(Box::new(EvalAltResult::ErrorDataTooLarge( + return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), _engine.limits.max_string_size, s.len(), Position::none(), - ))); + ).into(); } } } diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index d554b68e..34f6821d 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -41,13 +41,13 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { #[cfg(not(feature = "unchecked"))] if seconds > (MAX_INT as u64) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!( "Integer overflow for timestamp duration: {}", -(seconds as i64) ), Position::none(), - ))); + ).into(); } return Ok(-(seconds as INT)); @@ -62,10 +62,10 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { #[cfg(not(feature = "unchecked"))] if seconds > (MAX_INT as u64) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Integer overflow for timestamp duration: {}", seconds), Position::none(), - ))); + ).into(); } return Ok(seconds as INT); @@ -92,10 +92,10 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { #[cfg(not(feature = "unchecked"))] if seconds > (MAX_INT as u64) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( + return EvalAltResult::ErrorArithmetic( format!("Integer overflow for timestamp.elapsed: {}", seconds), Position::none(), - ))); + ).into(); } Ok(seconds as INT) diff --git a/src/result.rs b/src/result.rs index cecd14bd..40f2935c 100644 --- a/src/result.rs +++ b/src/result.rs @@ -361,3 +361,9 @@ impl EvalAltResult { self } } + +impl From for Result> { + fn from(err: EvalAltResult) -> Self { + Err(err.into()) + } +} diff --git a/src/serde/de.rs b/src/serde/de.rs index f6d328db..843c00d2 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -53,11 +53,12 @@ impl<'de> DynamicDeserializer<'de> { } /// Shortcut for a type conversion error. fn type_error_str(&self, error: &str) -> Result> { - Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( error.into(), self.value.type_name().into(), Position::none(), - ))) + ) + .into() } fn deserialize_int>( &mut self, @@ -134,10 +135,7 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>( impl Error for Box { fn custom(err: T) -> Self { - Box::new(EvalAltResult::ErrorParsing( - ParseErrorType::BadInput(err.to_string()), - Position::none(), - )) + EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none()) } } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index c11c7f1c..1f0d153d 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -99,10 +99,7 @@ pub fn to_dynamic(value: T) -> Result> impl Error for Box { fn custom(err: T) -> Self { - Box::new(EvalAltResult::ErrorRuntime( - err.to_string(), - Position::none(), - )) + EvalAltResult::ErrorRuntime(err.to_string(), Position::none()) } } @@ -298,22 +295,24 @@ impl Serializer for &mut DynamicSerializer { make_variant(_variant, content) } #[cfg(feature = "no_object")] - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( "Dynamic".into(), "map".into(), Position::none(), - ))); + ) + .into(); } fn serialize_seq(self, _len: Option) -> Result> { #[cfg(not(feature = "no_index"))] return Ok(DynamicSerializer::new(Array::new().into())); #[cfg(feature = "no_index")] - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( "Dynamic".into(), "array".into(), Position::none(), - ))); + ) + .into(); } fn serialize_tuple(self, len: usize) -> Result> { @@ -346,11 +345,12 @@ impl Serializer for &mut DynamicSerializer { let err_type = "map"; #[cfg(not(feature = "no_object"))] let err_type = "array"; - Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( "Dynamic".into(), err_type.into(), Position::none(), - ))) + ) + .into() } } @@ -358,11 +358,12 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(DynamicSerializer::new(Map::new().into())); #[cfg(feature = "no_object")] - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( "Dynamic".into(), "map".into(), Position::none(), - ))); + ) + .into(); } fn serialize_struct( @@ -386,11 +387,12 @@ impl Serializer for &mut DynamicSerializer { map: Map::with_capacity(_len), }); #[cfg(feature = "no_object")] - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( "Dynamic".into(), "map".into(), Position::none(), - ))); + ) + .into(); } } @@ -499,11 +501,11 @@ impl SerializeMap for DynamicSerializer { let key = mem::take(&mut self._key) .take_immutable_string() .map_err(|typ| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( "string".into(), typ.into(), Position::none(), - )) + ) })?; let _value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); @@ -523,11 +525,11 @@ impl SerializeMap for DynamicSerializer { { let _key: Dynamic = _key.serialize(&mut *self)?; let _key = _key.take_immutable_string().map_err(|typ| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( "string".into(), typ.into(), Position::none(), - )) + ) })?; let _value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); diff --git a/src/serde/str.rs b/src/serde/str.rs index 7fb1c9cc..8283cc79 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -20,11 +20,12 @@ impl<'a> ImmutableStringDeserializer<'a> { } /// Shortcut for a type conversion error. fn type_error(&self) -> Result> { - Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( type_name::().into(), "string".into(), Position::none(), - ))) + ) + .into() } } From 316918c741864ffc2d2a4cf98c72631b10f741a1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Aug 2020 10:17:40 +0800 Subject: [PATCH 15/15] no_closure when no_function. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 73cc5b2e..7dee574f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ only_i32 = [] # set INT=i32 (useful for 32-bit systems) only_i64 = [] # set INT=i64 (default) and disable support for all other integer types no_index = [] # no arrays and indexing no_object = [] # no custom objects -no_function = [] # no script-defined functions +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 internals = [] # expose internal data structures