From c530792d00ebbe93ca9d7cdff63895feb63c3f2e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 25 Jul 2020 16:36:50 +0800 Subject: [PATCH 1/6] Add repoTree and repoHome macros. --- doc/src/SUMMARY.md | 17 ++++++----- doc/src/about/license.md | 15 ++++++++++ doc/src/context.json | 2 ++ doc/src/start/examples/rust.md | 28 ++++++++--------- doc/src/start/examples/scripts.md | 50 +++++++++++++++---------------- no_std/no_std_test/Cargo.toml | 2 +- 6 files changed, 66 insertions(+), 48 deletions(-) create mode 100644 doc/src/about/license.md diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 4e41d940..b1e5a561 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -5,8 +5,9 @@ The Rhai Scripting Language 1. [Features](about/features.md) 2. [Supported Targets and Builds](about/targets.md) 3. [What Rhai Isn't](about/non-design.md) - 4. [Related Resources](about/related.md) -3. [Getting Started](start/index.md) + 4. [Licensing](about/license.md) + 5. [Related Resources](about/related.md) +2. [Getting Started](start/index.md) 1. [Online Playground](start/playground.md) 2. [Install the Rhai Crate](start/install.md) 3. [Optional Features](start/features.md) @@ -18,14 +19,14 @@ The Rhai Scripting Language 5. [Examples](start/examples/index.md) 1. [Rust](start/examples/rust.md) 2. [Scripts](start/examples/scripts.md) -4. [Using the `Engine`](engine/index.md) +3. [Using the `Engine`](engine/index.md) 1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md) 2. [Compile a Script to AST for Repeated Evaluations](engine/compile.md) 3. [Call a Rhai Function from Rust](engine/call-fn.md) 4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md) 5. [Evaluate Expressions Only](engine/expressions.md) 6. [Raw Engine](engine/raw.md) -5. [Extend Rhai with Rust](rust/index.md) +4. [Extend Rhai with Rust](rust/index.md) 1. [Traits](rust/traits.md) 2. [Register a Rust Function](rust/functions.md) 1. [String Parameters in Rust Functions](rust/strings.md) @@ -43,7 +44,7 @@ The Rhai Scripting Language 4. [Printing Custom Types](rust/print-custom.md) 9. [Scope - Initializing and Maintaining State](rust/scope.md) 10. [Engine Configuration Options](rust/options.md) -6. [Rhai Language Reference](language/index.md) +5. [Rhai Language Reference](language/index.md) 1. [Comments](language/comments.md) 2. [Values and Types](language/values-and-types.md) 1. [Dynamic Values](language/dynamic.md) @@ -85,7 +86,7 @@ The Rhai Scripting Language 4. [Create from AST](language/modules/ast.md) 5. [Module Resolvers](rust/modules/resolvers.md) 1. [Custom Implementation](rust/modules/imp-resolver.md) -7. [Safety and Protection](safety/index.md) +6. [Safety and Protection](safety/index.md) 1. [Checked Arithmetic](safety/checked.md) 2. [Sand-Boxing](safety/sandbox.md) 3. [Maximum Length of Strings](safety/max-string-size.md) @@ -96,7 +97,7 @@ The Rhai Scripting Language 7. [Maximum Number of Modules](safety/max-modules.md) 8. [Maximum Call Stack Depth](safety/max-call-stack.md) 9. [Maximum Statement Depth](safety/max-stmt-depth.md) -8. [Advanced Topics](advanced.md) +7. [Advanced Topics](advanced.md) 1. [Object-Oriented Programming (OOP)](language/oop.md) 2. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md) 3. [Script Optimization](engine/optimize/index.md) @@ -112,7 +113,7 @@ The Rhai Scripting Language 2. [Custom Operators](engine/custom-op.md) 3. [Extending with Custom Syntax](engine/custom-syntax.md) 6. [Eval Statement](language/eval.md) -9. [Appendix](appendix/index.md) +8. [Appendix](appendix/index.md) 1. [Keywords](appendix/keywords.md) 2. [Operators and Symbols](appendix/operators.md) 3. [Literals](appendix/literals.md) diff --git a/doc/src/about/license.md b/doc/src/about/license.md new file mode 100644 index 00000000..a60ac1eb --- /dev/null +++ b/doc/src/about/license.md @@ -0,0 +1,15 @@ +Licensing +========= + +{{#include ../links.md}} + +Rhai is licensed under either: + +* [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or +* [MIT license]({{repoHome}}/LICENSE-MIT.txt) + +at your option. + +Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate, +as defined in the Apache-2.0 license, shall be dual-licensed as above, +without any additional terms or conditions. diff --git a/doc/src/context.json b/doc/src/context.json index 025c159b..16f803db 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,7 @@ { "version": "0.18.0", + "repoHome": "https://github.com/jonathandturner/rhai/blob/master", + "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", "rootUrlX": "/rhai", "rootUrlXX": "/rhai/vnext" diff --git a/doc/src/start/examples/rust.md b/doc/src/start/examples/rust.md index 276b3d15..157cffed 100644 --- a/doc/src/start/examples/rust.md +++ b/doc/src/start/examples/rust.md @@ -5,17 +5,17 @@ Rust Examples A number of examples can be found in the `examples` directory: -| Example | Description | -| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. | -| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. | -| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | Simple example that evaluates an expression and prints the result. | -| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. | -| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. | -| [`serde`](https://github.com/jonathandturner/rhai/tree/master/examples/serde.rs) | Example to serialize and deserialize Rust types with [`serde`](https://crates.io/crates/serde).
The [`serde`] feature is required to run. | -| [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | Shows how to register a simple function. | -| [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | Shows different ways to register functions taking string arguments. | -| [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | A simple REPL, interactively evaluate statements from stdin. | +| Example | Description | +| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| [`arrays_and_structs`]({{repoTree}}/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. | +| [`custom_types_and_methods`]({{repoTree}}/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. | +| [`hello`]({{repoTree}}/examples/hello.rs) | Simple example that evaluates an expression and prints the result. | +| [`reuse_scope`]({{repoTree}}/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. | +| [`rhai_runner`]({{repoTree}}/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. | +| [`serde`]({{repoTree}}/examples/serde.rs) | Example to serialize and deserialize Rust types with [`serde`](https://crates.io/crates/serde).
The [`serde`] feature is required to run. | +| [`simple_fn`]({{repoTree}}/examples/simple_fn.rs) | Shows how to register a simple function. | +| [`strings`]({{repoTree}}/examples/strings.rs) | Shows different ways to register functions taking string arguments. | +| [`repl`]({{repoTree}}/examples/repl.rs) | A simple REPL, interactively evaluate statements from stdin. | The `repl` example is a particularly good one as it allows one to interactively try out Rhai's language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop). @@ -35,9 +35,9 @@ cargo run --example {example_name} To illustrate `no-std` builds, a number of sample applications are available under the `no_std` directory: -| Sample | Description | Optimization | Allocator | Panics | -| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | :----------: | :-----------------------------------------------: | :----: | -| [`no_std_test`](https://github.com/jonathandturner/rhai/tree/master/no_std/no_std_test) | Bare-bones test application that evaluates a Rhai expression and sets the result as the return value. | Size | [`wee_alloc`](https://crates.io/crates/wee_alloc) | Abort | +| Sample | Description | Optimization | Allocator | Panics | +| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | :----------: | :-----------------------------------------------: | :----: | +| [`no_std_test`]({{repoTree}}/no_std/no_std_test) | Bare-bones test application that evaluates a Rhai expression and sets the result as the return value. | Size | [`wee_alloc`](https://crates.io/crates/wee_alloc) | Abort | `cargo run` cannot be used to run a `no-std` sample. It must first be built: diff --git a/doc/src/start/examples/scripts.md b/doc/src/start/examples/scripts.md index 0875f869..57c38d13 100644 --- a/doc/src/start/examples/scripts.md +++ b/doc/src/start/examples/scripts.md @@ -8,25 +8,25 @@ Language Feature Scripts There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` directory: -| Script | Description | -| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] | -| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations | -| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments | -| [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`]({{rootUrl}}/language/for.md) loops | -| [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`]({{rootUrl}}/language/for.md) loops on [arrays] | -| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | A [function] without parameters | -| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | A [function] with two parameters | -| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | A [function] with many parameters | -| [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example | -| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | Count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop | -| [`oop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] | -| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition | -| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication | -| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis | -| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations | -| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations | -| [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`]({{rootUrl}}/language/while.md) loop | +| Script | Description | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| [`array.rhai`]({{repoTree}}/scripts/array.rhai) | [Arrays] | +| [`assignment.rhai`]({{repoTree}}/scripts/assignment.rhai) | Variable declarations | +| [`comments.rhai`]({{repoTree}}/scripts/comments.rhai) | Just comments | +| [`for1.rhai`]({{repoTree}}/scripts/for1.rhai) | [`for`]({{rootUrl}}/language/for.md) loops | +| [`for2.rhai`]({{repoTree}}/scripts/for2.rhai) | [`for`]({{rootUrl}}/language/for.md) loops on [arrays] | +| [`function_decl1.rhai`]({{repoTree}}/scripts/function_decl1.rhai) | A [function] without parameters | +| [`function_decl2.rhai`]({{repoTree}}/scripts/function_decl2.rhai) | A [function] with two parameters | +| [`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] | +| [`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 | +| [`string.rhai`]({{repoTree}}/scripts/string.rhai) | [String] operations | +| [`strings_map.rhai`]({{repoTree}}/scripts/strings_map.rhai) | [String] and [object map] operations | +| [`while.rhai`]({{repoTree}}/scripts/while.rhai) | [`while`]({{rootUrl}}/language/while.md) loop | Benchmark Scripts @@ -34,12 +34,12 @@ Benchmark Scripts The following scripts are for benchmarking the speed of Rhai: -| Scripts | Description | -| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | -| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | A simple application to measure the speed of Rhai's interpreter (1 million iterations). | -| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. | -| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. | -| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. | +| Scripts | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| [`speed_test.rhai`]({{repoTree}}/scripts/speed_test.rhai) | A simple application to measure the speed of Rhai's interpreter (1 million iterations). | +| [`primes.rhai`]({{repoTree}}/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. | +| [`fibonacci.rhai`]({{repoTree}}/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. | +| [`mat_mul.rhai`]({{repoTree}}/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. | Running Example Scripts diff --git a/no_std/no_std_test/Cargo.toml b/no_std/no_std_test/Cargo.toml index 4b151047..6e26639b 100644 --- a/no_std/no_std_test/Cargo.toml +++ b/no_std/no_std_test/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2018" authors = ["Stephen Chung"] description = "no-std test application" -homepage = "https://github.com/jonathandturner/rhai/tree/master/no_std/no_std_test" +homepage = "https://github.com/jonathandturner/rhai/tree/no_std/no_std_test" repository = "https://github.com/jonathandturner/rhai" [dependencies] From f601cc8c0a2d13e11fe0070dbc22f6c4b23c89ec Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 25 Jul 2020 16:51:29 +0800 Subject: [PATCH 2/6] Restore benchmark.yml. --- .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 353df6bea152184ad7fc3b3d511b8cee2f410d94 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 26 Jul 2020 10:03:59 +0800 Subject: [PATCH 3/6] Avoid warnings in standard build. --- src/engine.rs | 4 +--- src/fn_call.rs | 18 +++++++++++++++--- src/fn_native.rs | 9 ++++++--- src/packages/arithmetic.rs | 8 +++++++- src/packages/mod.rs | 1 + src/packages/time_basic.rs | 14 ++++++++------ src/syntax.rs | 7 +++++-- 7 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index a852b3fa..b7b73f2a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -639,9 +639,7 @@ impl Engine { ) .or_else(|err| match *err { // If there is no index setter, no need to set it back because the indexer is read-only - EvalAltResult::ErrorFunctionNotFound(s, _) - if s == FN_IDX_SET => - { + EvalAltResult::ErrorFunctionNotFound(_, _) => { Ok(Default::default()) } _ => Err(err), diff --git a/src/fn_call.rs b/src/fn_call.rs index 616fe2f6..98228853 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -864,10 +864,22 @@ impl Engine { EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => { Ok(def_val.unwrap().into()) } - EvalAltResult::ErrorFunctionNotFound(_, _) => { + EvalAltResult::ErrorFunctionNotFound(_, pos) => { Err(Box::new(EvalAltResult::ErrorFunctionNotFound( - format!("{}{}", modules, name), - Position::none(), + format!( + "{}{} ({})", + modules, + name, + args.iter() + .map(|a| if a.is::() { + "&str | ImmutableString | String" + } else { + self.map_type_name((*a).type_name()) + }) + .collect::>() + .join(", ") + ), + pos, ))) } _ => Err(err), diff --git a/src/fn_native.rs b/src/fn_native.rs index 2833729a..8fc60714 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -9,9 +9,12 @@ use crate::token::{is_valid_identifier, Position}; use crate::utils::{ImmutableString, StaticVec}; use crate::Scope; -use crate::stdlib::{ - boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc, vec::Vec, -}; +use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, mem, string::String, vec::Vec}; + +#[cfg(not(feature = "sync"))] +use crate::stdlib::rc::Rc; +#[cfg(feature = "sync")] +use crate::stdlib::sync::Arc; /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 5e43c05b..3e3852d6 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -20,9 +20,12 @@ use crate::stdlib::{ boxed::Box, fmt::Display, format, - ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, + ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Sub}, }; +#[cfg(feature = "unchecked")] +use crate::stdlib::ops::{Shl, Shr}; + // Checked add pub(crate) fn add(x: T, y: T) -> FuncReturn { x.checked_add(&y).ok_or_else(|| { @@ -171,10 +174,12 @@ pub(crate) fn shr(x: T, y: INT) -> FuncReturn { }) } // Unchecked left-shift - may panic if shifting by a negative number of bits +#[cfg(feature = "unchecked")] pub(crate) fn shl_u>(x: T, y: T) -> FuncReturn<>::Output> { Ok(x.shl(y)) } // Unchecked right-shift - may panic if shifting by a negative number of bits +#[cfg(feature = "unchecked")] pub(crate) fn shr_u>(x: T, y: T) -> FuncReturn<>::Output> { Ok(x.shr(y)) } @@ -229,6 +234,7 @@ pub(crate) fn pow_i_i(x: INT, y: INT) -> FuncReturn { } } // Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX) +#[cfg(feature = "unchecked")] pub(crate) fn pow_i_i_u(x: INT, y: INT) -> FuncReturn { Ok(x.pow(y as u32)) } diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 43ae5f28..8207a966 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -74,6 +74,7 @@ impl PackagesCollection { .flatten() } /// Does the specified TypeId iterator exist in the `PackagesCollection`? + #[allow(dead_code)] pub fn contains_iter(&self, id: TypeId) -> bool { self.0.iter().any(|p| p.contains_iter(id)) } diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index cbf13f1b..7c166ab1 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,12 +1,17 @@ #![cfg(not(feature = "no_std"))] use super::logic::{eq, gt, gte, lt, lte, ne}; + +#[cfg(feature = "no_float")] use super::math_basic::MAX_INT; use crate::def_package; -use crate::module::FuncReturn; -use crate::parser::INT; use crate::result::EvalAltResult; -use crate::token::Position; + +#[cfg(not(feature = "no_float"))] +use crate::parser::FLOAT; + +#[cfg(feature = "no_float")] +use crate::{module::FuncReturn, parser::INT, token::Position}; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::time::Instant; @@ -14,9 +19,6 @@ use crate::stdlib::time::Instant; #[cfg(target_arch = "wasm32")] use instant::Instant; -#[cfg(not(feature = "no_float"))] -use crate::parser::FLOAT; - def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { // Register date/time functions lib.set_fn_0("timestamp", || Ok(Instant::now())); diff --git a/src/syntax.rs b/src/syntax.rs index 616833c1..9dfc255f 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -14,11 +14,14 @@ use crate::utils::StaticVec; use crate::stdlib::{ boxed::Box, fmt, format, - rc::Rc, string::{String, ToString}, - sync::Arc, }; +#[cfg(not(feature = "sync"))] +use crate::stdlib::rc::Rc; +#[cfg(feature = "sync")] +use crate::stdlib::sync::Arc; + /// A general expression evaluation trait object. #[cfg(not(feature = "sync"))] pub type FnCustomSyntaxEval = dyn Fn( From 5e4847849671846e31c82a625e2e4066cd8ab78e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 26 Jul 2020 10:07:40 +0800 Subject: [PATCH 4/6] Improve writeup. --- README.md | 2 +- doc/src/SUMMARY.md | 21 ++++----- doc/src/about/index.md | 5 ++- doc/src/about/license.md | 3 +- doc/src/about/non-design.md | 23 ++++++++-- doc/src/advanced.md | 4 +- doc/src/engine/hello-world.md | 30 ++++++++----- doc/src/engine/raw.md | 9 ++-- doc/src/language/for.md | 3 +- doc/src/language/if.md | 3 ++ doc/src/language/logic.md | 39 ---------------- doc/src/language/method.md | 13 +++--- doc/src/language/other-op.md | 66 ++++++++++++++++++++++++++++ doc/src/rust/custom.md | 4 +- doc/src/rust/functions.md | 6 +-- doc/src/rust/modules/imp-resolver.md | 2 +- doc/src/rust/override.md | 4 +- doc/src/start/builds/performance.md | 4 +- doc/src/start/features.md | 4 +- 19 files changed, 156 insertions(+), 89 deletions(-) create mode 100644 doc/src/language/other-op.md diff --git a/README.md b/README.md index f5431f6e..1516abcc 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Licensed under either: * [Apache License, Version 2.0](https://github.com/jonathandturner/rhai/blob/master/LICENSE-APACHE.txt), or * [MIT license](https://github.com/jonathandturner/rhai/blob/master/LICENSE-MIT.txt) -at your option. +at your choice. Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate, as defined in the Apache-2.0 license, shall diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index b1e5a561..3592dbc5 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -21,7 +21,7 @@ The Rhai Scripting Language 2. [Scripts](start/examples/scripts.md) 3. [Using the `Engine`](engine/index.md) 1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md) - 2. [Compile a Script to AST for Repeated Evaluations](engine/compile.md) + 2. [Compile to AST for Repeated Evaluations](engine/compile.md) 3. [Call a Rhai Function from Rust](engine/call-fn.md) 4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md) 5. [Evaluate Expressions Only](engine/expressions.md) @@ -65,21 +65,22 @@ The Rhai Scripting Language 5. [Variables](language/variables.md) 6. [Constants](language/constants.md) 7. [Logic Operators](language/logic.md) - 8. [If Statement](language/if.md) - 9. [While Loop](language/while.md) - 10. [Loop Statement](language/loop.md) - 11. [For Loop](language/for.md) - 12. [Return Values](language/return.md) - 13. [Throw Exception on Error](language/throw.md) - 14. [Functions](language/functions.md) + 8. [Other Operators](language/other-op.md) + 9. [If Statement](language/if.md) + 10. [While Loop](language/while.md) + 11. [Loop Statement](language/loop.md) + 12. [For Loop](language/for.md) + 13. [Return Values](language/return.md) + 14. [Throw Exception on Error](language/throw.md) + 15. [Functions](language/functions.md) 1. [Call Method as Function](language/method.md) 2. [Overloading](language/overload.md) 3. [Namespaces](language/fn-namespaces.md) 4. [Function Pointers](language/fn-ptr.md) 5. [Anonymous Functions](language/fn-anon.md) 6. [Currying](language/fn-curry.md) - 15. [Print and Debug](language/print-debug.md) - 16. [Modules](language/modules/index.md) + 16. [Print and Debug](language/print-debug.md) + 17. [Modules](language/modules/index.md) 1. [Export Variables, Functions and Sub-Modules](language/modules/export.md) 2. [Import Modules](language/modules/import.md) 3. [Create from Rust](rust/modules/index.md) diff --git a/doc/src/about/index.md b/doc/src/about/index.md index 76603bde..cf021da5 100644 --- a/doc/src/about/index.md +++ b/doc/src/about/index.md @@ -7,6 +7,9 @@ Rhai is an embedded scripting language and evaluation engine for Rust that gives to add scripting to any application. -This Book is for version {{version}} of Rhai. +Versions +-------- + +This Book is for version **{{version}}** of Rhai. For the latest development version, see [here]({{rootUrl}}/vnext/). diff --git a/doc/src/about/license.md b/doc/src/about/license.md index a60ac1eb..a1f24339 100644 --- a/doc/src/about/license.md +++ b/doc/src/about/license.md @@ -6,9 +6,10 @@ Licensing Rhai is licensed under either: * [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or + * [MIT license]({{repoHome}}/LICENSE-MIT.txt) -at your option. +at your choice. Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate, as defined in the Apache-2.0 license, shall be dual-licensed as above, diff --git a/doc/src/about/non-design.md b/doc/src/about/non-design.md index 52888164..7f40614e 100644 --- a/doc/src/about/non-design.md +++ b/doc/src/about/non-design.md @@ -18,21 +18,38 @@ It doesn't attempt to be a new language. For example: * No first-class functions - Code your functions in Rust instead, and register them with Rhai. - There is, however, support for simple [function pointers] allowing runtime dispatch by function name. + There is, however, support for simple [function pointers] to allow runtime dispatch by function name. * No garbage collection - this should be expected, so... * No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md). + But you can [curry][currying] a [function pointer] with arguments to simulate it somewhat. + * No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not to be extremely _fast_, but to make it as easy as possible to integrate with native Rust applications. + +Do Not Write The Next 4D VR Game in Rhai +--------------------------------------- + Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting advanced language features such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc. Avoid the temptation to write full-fledge application logic entirely in Rhai - that use case is best fulfilled by more complete languages such as JavaScript or Lua. -Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call. -All your core functionalities should be in Rust. + +Thin Dynamic Wrapper Layer Over Rust Code +---------------------------------------- + +In actual practice, it is usually best to expose a Rust API into Rhai for scripts to call. + +All the core functionalities should be written in Rust, with Rhai being the dynamic _control_ layer. + This is similar to some dynamic languages where most of the core functionalities reside in a C/C++ standard library. + +Another similar scenario is a web front-end driving back-end services written in a systems language. +In this case, JavaScript takes the role of Rhai while the back-end language, well... it can actually also be Rust. +Except that Rhai integrates with Rust _much_ more tightly, removing the need for interfaces such +as XHR calls and payload encoding such as JSON. diff --git a/doc/src/advanced.md b/doc/src/advanced.md index b7d69e39..cd958c73 100644 --- a/doc/src/advanced.md +++ b/doc/src/advanced.md @@ -5,7 +5,7 @@ Advanced Topics This section covers advanced features such as: -* Simulated [Object Oriented Programming][OOP]. +* Simulated [Object Oriented Programming (OOP)][OOP]. * [`serde`] integration. @@ -13,4 +13,6 @@ This section covers advanced features such as: * [Domain-Specific Languages][DSL]. +* Low-level [function registration API]({{rootUrl}}/rust/register-raw.md) + * The dreaded (or beloved for those with twisted tastes) [`eval`] statement. diff --git a/doc/src/engine/hello-world.md b/doc/src/engine/hello-world.md index c4e868af..908a8cf8 100644 --- a/doc/src/engine/hello-world.md +++ b/doc/src/engine/hello-world.md @@ -22,19 +22,31 @@ fn main() -> Result<(), Box> } ``` -`rhai::EvalAltResult` is a Rust `enum` containing all errors encountered during the parsing or evaluation process. +Evaluate a script file directly: + +```rust +// 'eval_file' takes a 'PathBuf' +let result = engine.eval_file::("hello_world.rhai".into())?; +``` -Evaluate a Script ----------------- +Error Type +---------- -The type parameter is used to specify the type of the return value, which _must_ match the actual type or an error is returned. -Rhai is very strict here. +`rhai::EvalAltResult` is the standard Rhai error type, which is a Rust `enum` containing all errors encountered +during the parsing or evaluation process. -Use [`Dynamic`] for uncertain return types. + +Return Type +----------- + +The type parameter for `Engine::eval` is used to specify the type of the return value, +which _must_ match the actual type or an error is returned. Rhai is very strict here. There are two ways to specify the return type - _turbofish_ notation, or type inference. +Use [`Dynamic`] for uncertain return types. + ```rust let result = engine.eval::("40 + 2")?; // return type is i64, specified using 'turbofish' notation @@ -46,9 +58,3 @@ let result: Dynamic = engine.eval("boo()")?; // use 'Dynamic' if you're not s let result = engine.eval::("40 + 2")?; // returns an error because the actual return type is i64, not String ``` - -Evaluate a script file directly: - -```rust -let result = engine.eval_file::("hello_world.rhai".into())?; // 'eval_file' takes a 'PathBuf' -``` diff --git a/doc/src/engine/raw.md b/doc/src/engine/raw.md index ce61aa91..06e4ff33 100644 --- a/doc/src/engine/raw.md +++ b/doc/src/engine/raw.md @@ -10,7 +10,10 @@ In many controlled embedded environments, however, these may not be needed and u application code storage space. Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of -basic arithmetic and logical operators are supported. +basic arithmetic and logical operators are supported (see below). + +To add more functionalities to a _raw_ `Engine`, load [packages] into it. + Built-in Operators ------------------ @@ -20,7 +23,7 @@ Built-in Operators | `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` | | `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) | | `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` | -| `&`, `|`, | `&=`, `|=` | `INT`, `bool` | -| `&&`, `||` | | `bool` | +| `&`, \|, | `&=`, \|= | `INT`, `bool` | +| `&&`, \|\| | | `bool` | | `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` | | `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` | diff --git a/doc/src/language/for.md b/doc/src/language/for.md index 6780a1cd..15ea22e2 100644 --- a/doc/src/language/for.md +++ b/doc/src/language/for.md @@ -3,7 +3,8 @@ {{#include ../links.md}} -Iterating through a range or an [array] is provided by the `for` ... `in` loop. +Iterating through a range or an [array], or any type with a registered _iterator_, +is provided by the `for` ... `in` loop. Like C, `continue` can be used to skip to the next iteration, by-passing all following statements; `break` can be used to break out of the loop unconditionally. diff --git a/doc/src/language/if.md b/doc/src/language/if.md index 3958a2e3..b9c63dab 100644 --- a/doc/src/language/if.md +++ b/doc/src/language/if.md @@ -19,6 +19,9 @@ if foo(x) { } ``` +Braces Are Mandatory +-------------------- + Unlike C, the condition expression does _not_ need to be enclosed in parentheses '`(`' .. '`)`', but all branches of the `if` statement must be enclosed within braces '`{`' .. '`}`', even when there is only one statement inside the branch. diff --git a/doc/src/language/logic.md b/doc/src/language/logic.md index 113641f6..699b9827 100644 --- a/doc/src/language/logic.md +++ b/doc/src/language/logic.md @@ -65,42 +65,3 @@ a() | b(); // both a() and b() are evaluated a() & b(); // both a() and b() are evaluated ``` - -Compound Assignment Operators ----------------------------- - -```rust -let number = 9; - -number += 8; // number = number + 8 - -number -= 7; // number = number - 7 - -number *= 6; // number = number * 6 - -number /= 5; // number = number / 5 - -number %= 4; // number = number % 4 - -number ~= 3; // number = number ~ 3 - -number <<= 2; // number = number << 2 - -number >>= 1; // number = number >> 1 - -number &= 0x00ff; // number = number & 0x00ff; - -number |= 0x00ff; // number = number | 0x00ff; - -number ^= 0x00ff; // number = number ^ 0x00ff; -``` - -The `+=` operator can also be used to build [strings]: - -```rust -let my_str = "abc"; -my_str += "ABC"; -my_str += 12345; - -my_str == "abcABC12345" -``` diff --git a/doc/src/language/method.md b/doc/src/language/method.md index 6b6b9314..c2e9e99e 100644 --- a/doc/src/language/method.md +++ b/doc/src/language/method.md @@ -4,8 +4,8 @@ Call Method as Function {{#include ../links.md}} -First `&mut` Reference Parameter -------------------------------- +First `&mut` Parameter +---------------------- Property [getters/setters] and [methods][custom types] in a Rust custom type registered with the [`Engine`] can be called just like a regular function. In fact, like Rust, property getters/setters and object methods @@ -37,19 +37,20 @@ array[0].update(); // <- call in method-call style will update 'a' ``` -Encouraged Usage ----------------- +`&mut` is Efficient +------------------ Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone, even when the intention is not to mutate that argument, because it avoids cloning that argument value. -For primary types that are cheap to clone, including `ImmutableString`, this is not necessary. +For primary types that are cheap to clone (e.g. those that implement `Copy`), +including `ImmutableString`, this is not necessary. Avoid `&mut ImmutableString` --------------------------- -`ImmutableString`, Rhai internal [string] type, is an exception. +`ImmutableString`, Rhai's internal [string] type, is an exception. `ImmutableString` is cheap to clone, but expensive to take a mutable reference (because the underlying string must be cloned to make a private copy). diff --git a/doc/src/language/other-op.md b/doc/src/language/other-op.md new file mode 100644 index 00000000..bf5017d3 --- /dev/null +++ b/doc/src/language/other-op.md @@ -0,0 +1,66 @@ +Other Operators +=============== + +{{#include ../links.md}} + + +Compound Assignment Operators +---------------------------- + +```rust +let number = 9; + +number += 8; // number = number + 8 + +number -= 7; // number = number - 7 + +number *= 6; // number = number * 6 + +number /= 5; // number = number / 5 + +number %= 4; // number = number % 4 + +number ~= 3; // number = number ~ 3 + +number <<= 2; // number = number << 2 + +number >>= 1; // number = number >> 1 + +number &= 0x00ff; // number = number & 0x00ff; + +number |= 0x00ff; // number = number | 0x00ff; + +number ^= 0x00ff; // number = number ^ 0x00ff; +``` + + +The Flexible `+=` +---------------- + +The `+=` operator can also be used to build [strings]: + +```rust +let my_str = "abc"; +my_str += "ABC"; +my_str += 12345; + +my_str == "abcABC12345" +``` + +It may also be used to concatenate [arrays]: + +```rust +let my_array = [1, 2, 3]; +my_array += [4, 5]; + +my_array == [1, 2, 3, 4, 5]; +``` + +or mix two [object maps] together: + +```rust +let my_obj = #{a:1, b:2}; +my_obj += #{c:3, d:4, e:5}; + +my_obj.len() == 5; +``` diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index 7460f50e..3e6b5ca3 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -66,8 +66,8 @@ let mut engine = Engine::new(); engine.register_type::(); ``` -Methods on Custom Type ---------------------- +Methods on The Custom Type +------------------------- To use native custom types, methods and functions in Rhai scripts, simply register them using one of the `Engine::register_XXX` API. diff --git a/doc/src/rust/functions.md b/doc/src/rust/functions.md index a929484c..8d8666cf 100644 --- a/doc/src/rust/functions.md +++ b/doc/src/rust/functions.md @@ -11,8 +11,8 @@ see [fallible functions]({{rootUrl}}/rust/fallible.md)). ```rust use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString}; -use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn' -use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn' +use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn' +use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn' // Normal function that returns a standard type // Remember to use 'ImmutableString' and not 'String' @@ -26,7 +26,7 @@ fn add_len_str(x: i64, s: &str) -> i64 { // Function that returns a 'Dynamic' value - must return a 'Result' fn get_any_value() -> Result> { - Ok((42_i64).into()) // standard types can use 'into()' + Ok((42_i64).into()) // standard types can use 'into()' } let mut engine = Engine::new(); diff --git a/doc/src/rust/modules/imp-resolver.md b/doc/src/rust/modules/imp-resolver.md index 415abf72..55d68315 100644 --- a/doc/src/rust/modules/imp-resolver.md +++ b/doc/src/rust/modules/imp-resolver.md @@ -30,7 +30,7 @@ impl ModuleResolver for MyModuleResolver { &self, engine: &Engine, // reference to the current 'Engine' path: &str, // the module path - pos: Position, // location of the 'import' statement + pos: Position, // position of the 'import' statement ) -> Result> { // Check module path. if is_valid_module_path(path) { diff --git a/doc/src/rust/override.md b/doc/src/rust/override.md index 576b5ad8..f6d4d656 100644 --- a/doc/src/rust/override.md +++ b/doc/src/rust/override.md @@ -12,7 +12,9 @@ fn to_int(num) { print("Ha! Gotcha! " + num); } -print(to_int(123)); // what happens? +let x = (123).to_int(); + +print(x); // what happens? ``` A registered native Rust function, in turn, overrides any built-in function of the diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md index dc47a4cc..27747208 100644 --- a/doc/src/start/builds/performance.md +++ b/doc/src/start/builds/performance.md @@ -37,8 +37,8 @@ Use `ImmutableString` Internally, Rhai uses _immutable_ [strings] instead of the Rust `String` type. This is mainly to avoid excessive cloning when passing function arguments. -The encapsulated immutable string type is `ImmutableString`. It is cheap to clone (just an `Rc` or `Arc` reference -count increment depending on the [`sync`] feature). +Rhai's internal string type is `ImmutableString` (basically `Rc` or `Arc` depending on the [`sync`] feature). +It is cheap to clone, but expensive to modify (a new copy of the string must be made in order to change it). Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`) for the best performance with Rhai. diff --git a/doc/src/start/features.md b/doc/src/start/features.md index 023834fd..f2286a38 100644 --- a/doc/src/start/features.md +++ b/doc/src/start/features.md @@ -25,7 +25,7 @@ more control over what a script can (or cannot) do. | `no_module` | Disable loading external [modules]. | | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | | `serde` | Enable serialization/deserialization via [`serde`]. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. | -| `internals` | Expose internal data structures (e.g. [`AST`] nodes) and enable defining [custom syntax]. Beware that Rhai internals are volatile and may change from version to version. | +| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. | Example @@ -46,7 +46,7 @@ rhai = { version = "{{version}}", features = [ "sync", "unchecked", "only_i32", ``` The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32`, `i16` or `i64`), -no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining [functions] +no floating-point, is `Send + Sync` (so it can be safely used across threads), and does not support defining [functions] nor loading external [modules]. This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware. From 6b600704a3a40946bde12e6298eb6c2fefb8b509 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 26 Jul 2020 15:53:22 +0800 Subject: [PATCH 5/6] Avoid warnings. --- src/any.rs | 8 +- src/api.rs | 19 +-- src/engine.rs | 270 +++++++++++++++++++++++------------- src/fn_args.rs | 7 +- src/fn_call.rs | 81 ++++++----- src/fn_native.rs | 19 ++- src/fn_register.rs | 5 +- src/lib.rs | 2 +- src/module.rs | 61 +++++--- src/optimize.rs | 32 +++-- src/packages/arithmetic.rs | 37 +++-- src/packages/array_basic.rs | 19 ++- src/packages/map_basic.rs | 8 +- src/packages/math_basic.rs | 12 +- src/packages/mod.rs | 1 - src/packages/string_more.rs | 16 ++- src/packages/time_basic.rs | 7 +- src/parser.rs | 127 ++++++++++++++--- src/result.rs | 4 +- src/scope.rs | 2 + src/serde/de.rs | 33 +++-- src/serde/ser.rs | 121 ++++++++-------- src/serde/str.rs | 2 +- src/settings.rs | 41 +++--- src/token.rs | 5 +- tests/call_fn.rs | 4 +- tests/modules.rs | 2 +- 27 files changed, 624 insertions(+), 321 deletions(-) diff --git a/src/any.rs b/src/any.rs index 1d478e62..9d28c768 100644 --- a/src/any.rs +++ b/src/any.rs @@ -16,12 +16,16 @@ use crate::engine::Map; use crate::stdlib::{ any::{type_name, Any, TypeId}, boxed::Box, - collections::HashMap, fmt, string::String, - vec::Vec, }; +#[cfg(not(feature = "no_object"))] +use crate::stdlib::collections::HashMap; + +#[cfg(not(feature = "no_index"))] +use crate::stdlib::vec::Vec; + #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::time::Instant; diff --git a/src/api.rs b/src/api.rs index c4c91f38..b7a1bb74 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,30 +1,33 @@ //! Module that defines the extern API of `Engine`. use crate::any::{Dynamic, Variant}; -use crate::engine::{make_getter, make_setter, Engine, Imports, State, FN_IDX_GET, FN_IDX_SET}; +use crate::engine::{Engine, Imports, State}; use crate::error::ParseError; -use crate::fn_args::FuncArgs; use crate::fn_native::{IteratorFn, SendSync}; -use crate::fn_register::RegisterFn; use crate::module::{FuncReturn, Module}; use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::parser::AST; use crate::result::EvalAltResult; use crate::scope::Scope; use crate::token::{lex, Position}; -use crate::utils::StaticVec; + +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +use crate::engine::{FN_IDX_GET, FN_IDX_SET}; #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::{ + engine::{make_getter, make_setter, Map}, + fn_register::RegisterFn, +}; #[cfg(not(feature = "no_function"))] -use crate::engine::get_script_function_by_signature; +use crate::{engine::get_script_function_by_signature, fn_args::FuncArgs, utils::StaticVec}; use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, mem, - string::{String, ToString}, }; #[cfg(not(feature = "no_std"))] @@ -293,7 +296,7 @@ impl Engine { self.type_names .as_mut() .unwrap() - .insert(type_name::().to_string(), name.to_string()); + .insert(type_name::().into(), name.into()); self } diff --git a/src/engine.rs b/src/engine.rs index b7b73f2a..f4435698 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,13 +1,13 @@ //! Main module defining the script evaluation `Engine`. -use crate::any::{map_std_type_name, Dynamic, Union, Variant}; +use crate::any::{map_std_type_name, Dynamic, Union}; use crate::calc_fn_hash; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{CallableFunction, Callback, FnPtr}; -use crate::module::{resolvers, Module, ModuleRef, ModuleResolver}; +use crate::module::{Module, ModuleRef}; use crate::optimize::OptimizationLevel; use crate::packages::{Package, PackagesCollection, StandardPackage}; -use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, Stmt}; +use crate::parser::{Expr, ReturnType, Stmt}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; @@ -15,8 +15,23 @@ use crate::syntax::{CustomSyntax, EvalContext}; use crate::token::Position; use crate::utils::StaticVec; +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +use crate::any::Variant; + +#[cfg(not(feature = "no_function"))] +use crate::parser::{FnAccess, ScriptFnDef}; + +#[cfg(not(feature = "no_module"))] +use crate::module::ModuleResolver; + +#[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_module"))] +use crate::module::resolvers; + +#[cfg(any(not(feature = "no_object"), not(feature = "no_module")))] +use crate::utils::ImmutableString; + use crate::stdlib::{ - any::TypeId, borrow::Cow, boxed::Box, collections::{HashMap, HashSet}, @@ -26,6 +41,9 @@ use crate::stdlib::{ vec::Vec, }; +#[cfg(not(feature = "no_index"))] +use crate::stdlib::any::TypeId; + /// Variable-sized array of `Dynamic` values. /// /// Not available under the `no_index` feature. @@ -66,13 +84,6 @@ pub const MAX_EXPR_DEPTH: usize = 128; #[cfg(not(debug_assertions))] pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; -#[cfg(feature = "unchecked")] -pub const MAX_CALL_STACK_DEPTH: usize = usize::MAX; -#[cfg(feature = "unchecked")] -pub const MAX_EXPR_DEPTH: usize = 0; -#[cfg(feature = "unchecked")] -pub const MAX_FUNCTION_EXPR_DEPTH: usize = 0; - pub const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_TYPE_OF: &str = "type_of"; @@ -82,16 +93,22 @@ pub const KEYWORD_FN_PTR_CALL: &str = "call"; pub const KEYWORD_FN_PTR_CURRY: &str = "curry"; pub const KEYWORD_THIS: &str = "this"; pub const FN_TO_STRING: &str = "to_string"; +#[cfg(not(feature = "no_object"))] pub const FN_GET: &str = "get$"; +#[cfg(not(feature = "no_object"))] pub const FN_SET: &str = "set$"; +#[cfg(not(feature = "no_index"))] pub const FN_IDX_GET: &str = "index$get$"; +#[cfg(not(feature = "no_index"))] pub const FN_IDX_SET: &str = "index$set$"; +#[cfg(not(feature = "no_function"))] pub const FN_ANONYMOUS: &str = "anon$"; pub const MARKER_EXPR: &str = "$expr$"; pub const MARKER_BLOCK: &str = "$block$"; pub const MARKER_IDENT: &str = "$ident$"; /// A type specifying the method of chaining. +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum ChainType { None, @@ -100,6 +117,7 @@ pub enum ChainType { } /// A type that encapsulates a mutation target for an expression with side effects. +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug)] pub enum Target<'a> { /// The target is a mutable reference to a `Dynamic` value somewhere. @@ -108,15 +126,19 @@ pub enum Target<'a> { Value(Dynamic), /// The target is a character inside a String. /// This is necessary because directly pointing to a char inside a String is impossible. + #[cfg(not(feature = "no_index"))] StringChar(&'a mut Dynamic, usize, Dynamic), } +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl Target<'_> { /// Is the `Target` a reference pointing to other data? pub fn is_ref(&self) -> bool { match self { Self::Ref(_) => true, - Self::Value(_) | Self::StringChar(_, _, _) => false, + Self::Value(_) => false, + #[cfg(not(feature = "no_index"))] + Self::StringChar(_, _, _) => false, } } /// Is the `Target` an owned value? @@ -124,22 +146,26 @@ impl Target<'_> { match self { Self::Ref(_) => false, Self::Value(_) => true, + #[cfg(not(feature = "no_index"))] Self::StringChar(_, _, _) => false, } } /// Is the `Target` a specific type? + #[allow(dead_code)] pub fn is(&self) -> bool { match self { Target::Ref(r) => r.is::(), Target::Value(r) => r.is::(), + #[cfg(not(feature = "no_index"))] Target::StringChar(_, _, _) => TypeId::of::() == TypeId::of::(), } } /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. pub fn clone_into_dynamic(self) -> Dynamic { match self { - Self::Ref(r) => r.clone(), // Referenced value is cloned - Self::Value(v) => v, // Owned value is simply taken + Self::Ref(r) => r.clone(), // Referenced value is cloned + Self::Value(v) => v, // Owned value is simply taken + #[cfg(not(feature = "no_index"))] Self::StringChar(_, _, ch) => ch, // Character is taken } } @@ -148,6 +174,7 @@ impl Target<'_> { match self { Self::Ref(r) => *r, Self::Value(ref mut r) => r, + #[cfg(not(feature = "no_index"))] Self::StringChar(_, _, ref mut r) => r, } } @@ -161,6 +188,7 @@ impl Target<'_> { Position::none(), ))) } + #[cfg(not(feature = "no_index"))] Self::StringChar(Dynamic(Union::Str(ref mut s)), index, _) => { // Replace the character at the specified index position let new_ch = new_val @@ -176,18 +204,21 @@ impl Target<'_> { *s = chars.iter().collect::().into(); } } - _ => unreachable!(), + #[cfg(not(feature = "no_index"))] + Self::StringChar(_, _, _) => unreachable!(), } Ok(()) } } +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl<'a> From<&'a mut Dynamic> for Target<'a> { fn from(value: &'a mut Dynamic) -> Self { Self::Ref(value) } } +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl> From for Target<'_> { fn from(value: T) -> Self { Self::Value(value.into()) @@ -249,6 +280,34 @@ pub fn get_script_function_by_signature<'a>( } } +/// [INTERNALS] A type containing all the limits imposed by the `Engine`. +/// Exported under the `internals` feature only. +/// +/// ## WARNING +/// +/// This type is volatile and may change. +#[cfg(not(feature = "unchecked"))] +pub struct Limits { + /// Maximum levels of call-stack to prevent infinite recursion. + /// + /// Defaults to 16 for debug builds and 128 for non-debug builds. + pub max_call_stack_depth: usize, + /// Maximum depth of statements/expressions at global level. + pub max_expr_depth: usize, + /// Maximum depth of statements/expressions in functions. + pub max_function_expr_depth: usize, + /// Maximum number of operations allowed to run. + pub max_operations: u64, + /// Maximum number of modules allowed to load. + pub max_modules: usize, + /// Maximum length of a string. + pub max_string_size: usize, + /// Maximum length of an array. + pub max_array_size: usize, + /// Maximum number of properties in a map. + pub max_map_size: usize, +} + /// Rhai main scripting engine. /// /// ``` @@ -275,6 +334,7 @@ pub struct Engine { pub(crate) packages: PackagesCollection, /// A module resolution service. + #[cfg(not(feature = "no_module"))] pub(crate) module_resolver: Option>, /// A hashmap mapping type names to pretty-print names. @@ -296,24 +356,10 @@ pub struct Engine { /// Optimize the AST after compilation. pub(crate) optimization_level: OptimizationLevel, - /// Maximum levels of call-stack to prevent infinite recursion. - /// - /// Defaults to 16 for debug builds and 128 for non-debug builds. - pub(crate) max_call_stack_depth: usize, - /// Maximum depth of statements/expressions at global level. - pub(crate) max_expr_depth: usize, - /// Maximum depth of statements/expressions in functions. - pub(crate) max_function_expr_depth: usize, - /// Maximum number of operations allowed to run. - pub(crate) max_operations: u64, - /// Maximum number of modules allowed to load. - pub(crate) max_modules: usize, - /// Maximum length of a string. - pub(crate) max_string_size: usize, - /// Maximum length of an array. - pub(crate) max_array_size: usize, - /// Maximum number of properties in a map. - pub(crate) max_map_size: usize, + + /// Max limits. + #[cfg(not(feature = "unchecked"))] + pub(crate) limits: Limits, } impl fmt::Debug for Engine { @@ -338,7 +384,8 @@ impl Default for Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())), - #[cfg(any(feature = "no_module", feature = "no_std", target_arch = "wasm32",))] + #[cfg(not(feature = "no_module"))] + #[cfg(any(feature = "no_std", target_arch = "wasm32",))] module_resolver: None, type_names: None, @@ -360,14 +407,17 @@ impl Default for Engine { #[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel::Simple, - max_call_stack_depth: MAX_CALL_STACK_DEPTH, - max_expr_depth: MAX_EXPR_DEPTH, - max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, - max_operations: 0, - max_modules: usize::MAX, - max_string_size: 0, - max_array_size: 0, - max_map_size: 0, + #[cfg(not(feature = "unchecked"))] + limits: Limits { + max_call_stack_depth: MAX_CALL_STACK_DEPTH, + max_expr_depth: MAX_EXPR_DEPTH, + max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, + max_operations: 0, + max_modules: usize::MAX, + max_string_size: 0, + max_array_size: 0, + max_map_size: 0, + }, }; engine.load_package(StandardPackage::new().get()); @@ -377,20 +427,24 @@ impl Default for Engine { } /// Make getter function +#[cfg(not(feature = "no_object"))] +#[inline(always)] pub fn make_getter(id: &str) -> String { format!("{}{}", FN_GET, id) } /// Make setter function +#[cfg(not(feature = "no_object"))] +#[inline(always)] pub fn make_setter(id: &str) -> String { format!("{}{}", FN_SET, id) } /// Print/debug to stdout -fn default_print(s: &str) { +fn default_print(_s: &str) { #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] - println!("{}", s); + println!("{}", _s); } /// Search for a module within an imports stack. @@ -546,6 +600,8 @@ impl Engine { packages: Default::default(), global_module: Default::default(), + + #[cfg(not(feature = "no_module"))] module_resolver: None, type_names: None, @@ -563,19 +619,23 @@ impl Engine { #[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel::Simple, - max_call_stack_depth: MAX_CALL_STACK_DEPTH, - max_expr_depth: MAX_EXPR_DEPTH, - max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, - max_operations: 0, - max_modules: usize::MAX, - max_string_size: 0, - max_array_size: 0, - max_map_size: 0, + #[cfg(not(feature = "unchecked"))] + limits: Limits { + max_call_stack_depth: MAX_CALL_STACK_DEPTH, + max_expr_depth: MAX_EXPR_DEPTH, + max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, + max_operations: 0, + max_modules: usize::MAX, + max_string_size: 0, + max_array_size: 0, + max_map_size: 0, + }, } } /// Chain-evaluate a dot/index chain. /// Position in `EvalAltResult` is `None` and must be set afterwards. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_dot_index_chain_helper( &self, state: &mut State, @@ -586,7 +646,7 @@ impl Engine { idx_values: &mut StaticVec, chain_type: ChainType, level: usize, - mut new_val: Option, + mut _new_val: Option, ) -> Result<(Dynamic, bool), Box> { if chain_type == ChainType::None { panic!(); @@ -618,18 +678,19 @@ impl Engine { self.eval_dot_index_chain_helper( state, lib, this_ptr, obj_ptr, expr, idx_values, next_chain, level, - new_val, + _new_val, ) .map_err(|err| err.new_position(*pos)) } // xxx[rhs] = new_val - _ if new_val.is_some() => { - let mut new_val = new_val.unwrap(); + _ if _new_val.is_some() => { + let mut new_val = _new_val.unwrap(); let mut idx_val2 = idx_val.clone(); match self.get_indexed_mut(state, lib, target, idx_val, pos, true, level) { // Indexed value is an owned value - the only possibility is an indexer // Try to call an index setter + #[cfg(not(feature = "no_index"))] Ok(obj_ptr) if obj_ptr.is_value() => { let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val]; @@ -653,6 +714,7 @@ impl Engine { } Err(err) => match *err { // No index getter - try to call an index setter + #[cfg(not(feature = "no_index"))] EvalAltResult::ErrorIndexingType(_, _) => { let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val]; @@ -684,13 +746,13 @@ impl Engine { // xxx.module::fn_name(...) - syntax error Expr::FnCall(_) => unreachable!(), // {xxx:map}.id = ??? - Expr::Property(x) if target.is::() && new_val.is_some() => { + Expr::Property(x) if target.is::() && _new_val.is_some() => { let ((prop, _, _), pos) = x.as_ref(); let index = prop.clone().into(); let mut val = self.get_indexed_mut(state, lib, target, index, *pos, true, level)?; - val.set_value(new_val.unwrap()) + val.set_value(_new_val.unwrap()) .map_err(|err| err.new_position(rhs.position()))?; Ok((Default::default(), true)) } @@ -704,9 +766,9 @@ impl Engine { Ok((val.clone_into_dynamic(), false)) } // xxx.id = ??? - Expr::Property(x) if new_val.is_some() => { + Expr::Property(x) if _new_val.is_some() => { let ((_, _, setter), pos) = x.as_ref(); - let mut args = [target.as_mut(), new_val.as_mut().unwrap()]; + let mut args = [target.as_mut(), _new_val.as_mut().unwrap()]; self.exec_fn_call( state, lib, setter, true, 0, &mut args, is_ref, true, None, level, ) @@ -748,7 +810,7 @@ impl Engine { self.eval_dot_index_chain_helper( state, lib, this_ptr, &mut val, expr, idx_values, next_chain, level, - new_val, + _new_val, ) .map_err(|err| err.new_position(*pos)) } @@ -775,7 +837,7 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( state, lib, this_ptr, target, expr, idx_values, next_chain, - level, new_val, + level, _new_val, ) .map_err(|err| err.new_position(*pos))?; @@ -810,7 +872,7 @@ impl Engine { self.eval_dot_index_chain_helper( state, lib, this_ptr, target, expr, idx_values, next_chain, - level, new_val, + level, _new_val, ) .map_err(|err| err.new_position(*pos)) } @@ -833,6 +895,7 @@ impl Engine { } /// Evaluate a dot/index chain. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_dot_index_chain( &self, scope: &mut Scope, @@ -909,6 +972,7 @@ impl Engine { /// Any spill-overs are stored in `more`, which is dynamic. /// The fixed length array is used to avoid an allocation in the overwhelming cases of just a few levels of indexing. /// The total number of values is returned. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_indexed_chain( &self, scope: &mut Scope, @@ -979,26 +1043,30 @@ impl Engine { /// Get the value at the indexed position of a base type /// Position in `EvalAltResult` may be None and should be set afterwards. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn get_indexed_mut<'a>( &self, state: &mut State, - lib: &Module, + _lib: &Module, target: &'a mut Target, - mut idx: Dynamic, + mut _idx: Dynamic, idx_pos: Position, - create: bool, - level: usize, + _create: bool, + _level: usize, ) -> Result, Box> { self.inc_operations(state)?; + #[cfg(not(feature = "no_index"))] + #[cfg(not(feature = "no_object"))] let is_ref = target.is_ref(); + let val = target.as_mut(); match val { #[cfg(not(feature = "no_index"))] Dynamic(Union::Array(arr)) => { // val_array[idx] - let index = idx + let index = _idx .as_int() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_pos))?; @@ -1020,14 +1088,14 @@ impl Engine { #[cfg(not(feature = "no_object"))] Dynamic(Union::Map(map)) => { // val_map[idx] - Ok(if create { - let index = idx + Ok(if _create { + let index = _idx .take_immutable_string() .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_pos))?; map.entry(index).or_insert(Default::default()).into() } else { - let index = idx + let index = _idx .downcast_ref::() .ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?; @@ -1041,7 +1109,7 @@ impl Engine { Dynamic(Union::Str(s)) => { // val_string[idx] let chars_len = s.chars().count(); - let index = idx + let index = _idx .as_int() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_pos))?; @@ -1062,9 +1130,9 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ => { let type_name = val.type_name(); - let args = &mut [val, &mut idx]; + let args = &mut [val, &mut _idx]; self.exec_fn_call( - state, lib, FN_IDX_GET, true, 0, args, is_ref, true, None, level, + state, _lib, FN_IDX_GET, true, 0, args, is_ref, true, None, _level, ) .map(|(v, _)| v.into()) .map_err(|err| match *err { @@ -1075,7 +1143,7 @@ impl Engine { }) } - #[cfg(feature = "no_index")] + #[cfg(any(feature = "no_index", feature = "no_object"))] _ => Err(Box::new(EvalAltResult::ErrorIndexingType( self.map_type_name(val.type_name()).into(), Position::none(), @@ -1252,7 +1320,7 @@ impl Engine { let mut rhs_val = self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?; - let new_val = Some(if op.is_empty() { + let _new_val = Some(if op.is_empty() { // Normal assignment rhs_val } else { @@ -1275,7 +1343,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Index(_) => { self.eval_dot_index_chain( - scope, mods, state, lib, this_ptr, lhs_expr, level, new_val, + scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; Ok(Default::default()) } @@ -1283,7 +1351,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] Expr::Dot(_) => { self.eval_dot_index_chain( - scope, mods, state, lib, this_ptr, lhs_expr, level, new_val, + scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; Ok(Default::default()) } @@ -1639,19 +1707,20 @@ impl Engine { Stmt::Const(_) => unreachable!(), // Import statement + #[cfg(not(feature = "no_module"))] Stmt::Import(x) => { - let (expr, (name, pos)) = x.as_ref(); + let (expr, (name, _pos)) = x.as_ref(); // Guard against too many modules - if state.modules >= self.max_modules { - return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos))); + #[cfg(not(feature = "unchecked"))] + if state.modules >= self.limits.max_modules { + return Err(Box::new(EvalAltResult::ErrorTooManyModules(*_pos))); } if let Some(path) = self .eval_expr(scope, mods, state, lib, this_ptr, &expr, level)? .try_cast::() { - #[cfg(not(feature = "no_module"))] if let Some(resolver) = &self.module_resolver { let mut module = resolver.resolve(self, &path, expr.position())?; module.index_all_sub_modules(); @@ -1666,15 +1735,13 @@ impl Engine { expr.position(), ))) } - - #[cfg(feature = "no_module")] - Ok(Default::default()) } else { Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) } } // Export statement + #[cfg(not(feature = "no_module"))] Stmt::Export(list) => { for ((id, id_pos), rename) in list.iter() { // Mark scope variables as public @@ -1696,8 +1763,18 @@ impl Engine { .map_err(|err| err.new_position(stmt.position())) } + #[cfg(feature = "unchecked")] + #[inline(always)] + fn check_data_size( + &self, + result: Result>, + ) -> Result> { + return result; + } + /// Check a result to ensure that the data size is within allowable limit. /// Position in `EvalAltResult` may be None and should be set afterwards. + #[cfg(not(feature = "unchecked"))] fn check_data_size( &self, result: Result>, @@ -1706,7 +1783,8 @@ impl Engine { return result; // If no data size limits, just return - if self.max_string_size + self.max_array_size + self.max_map_size == 0 { + if self.limits.max_string_size + self.limits.max_array_size + self.limits.max_map_size == 0 + { return result; } @@ -1766,37 +1844,37 @@ impl Engine { // Simply return all errors Err(_) => return result, // String with limit - Ok(Dynamic(Union::Str(_))) if self.max_string_size > 0 => (), + Ok(Dynamic(Union::Str(_))) if self.limits.max_string_size > 0 => (), // Array with limit #[cfg(not(feature = "no_index"))] - Ok(Dynamic(Union::Array(_))) if self.max_array_size > 0 => (), + Ok(Dynamic(Union::Array(_))) if self.limits.max_array_size > 0 => (), // Map with limit #[cfg(not(feature = "no_object"))] - Ok(Dynamic(Union::Map(_))) if self.max_map_size > 0 => (), + Ok(Dynamic(Union::Map(_))) if self.limits.max_map_size > 0 => (), // Everything else is simply returned Ok(_) => return result, }; let (arr, map, s) = calc_size(result.as_ref().unwrap()); - if s > self.max_string_size { + if s > self.limits.max_string_size { Err(Box::new(EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - self.max_string_size, + self.limits.max_string_size, s, Position::none(), ))) - } else if arr > self.max_array_size { + } else if arr > self.limits.max_array_size { Err(Box::new(EvalAltResult::ErrorDataTooLarge( "Size of array".to_string(), - self.max_array_size, + self.limits.max_array_size, arr, Position::none(), ))) - } else if map > self.max_map_size { + } else if map > self.limits.max_map_size { Err(Box::new(EvalAltResult::ErrorDataTooLarge( "Number of properties in object map".to_string(), - self.max_map_size, + self.limits.max_map_size, map, Position::none(), ))) @@ -1812,7 +1890,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] // Guard against too many operations - if self.max_operations > 0 && state.operations > self.max_operations { + if self.limits.max_operations > 0 && state.operations > self.limits.max_operations { return Err(Box::new(EvalAltResult::ErrorTooManyOperations( Position::none(), ))); diff --git a/src/fn_args.rs b/src/fn_args.rs index 833014bb..e5a22390 100644 --- a/src/fn_args.rs +++ b/src/fn_args.rs @@ -22,11 +22,10 @@ macro_rules! impl_args { fn into_vec(self) -> StaticVec { let ($($p,)*) = self; - #[allow(unused_mut)] - let mut v = StaticVec::new(); - $(v.push($p.into_dynamic());)* + let mut _v = StaticVec::new(); + $(_v.push($p.into_dynamic());)* - v + _v } } diff --git a/src/fn_call.rs b/src/fn_call.rs index 98228853..c55124e2 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -3,26 +3,34 @@ use crate::any::Dynamic; use crate::calc_fn_hash; use crate::engine::{ - search_imports, search_namespace, search_scope_only, Engine, Imports, State, Target, FN_GET, - FN_IDX_GET, FN_IDX_SET, FN_SET, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, - KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_PRINT, KEYWORD_TYPE_OF, + search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG, + KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_PRINT, + KEYWORD_TYPE_OF, }; use crate::error::ParseErrorType; use crate::fn_native::{FnCallArgs, FnPtr}; use crate::module::{Module, ModuleRef}; use crate::optimize::OptimizationLevel; -use crate::parser::{Expr, ImmutableString, ScriptFnDef, AST, INT}; -use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; +use crate::parser::{Expr, ImmutableString, AST, INT}; use crate::result::EvalAltResult; -use crate::scope::{EntryType as ScopeEntryType, Scope}; +use crate::scope::Scope; use crate::token::Position; use crate::utils::StaticVec; +#[cfg(not(feature = "no_function"))] +use crate::{ + parser::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime, + scope::EntryType as ScopeEntryType, +}; + #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; +#[cfg(not(feature = "no_index"))] +use crate::engine::{FN_IDX_GET, FN_IDX_SET}; + #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::engine::{Map, Target, FN_GET, FN_SET}; use crate::stdlib::{ any::{type_name, TypeId}, @@ -36,20 +44,22 @@ use crate::stdlib::{ }; /// Extract the property name from a getter function name. -fn extract_prop_from_getter(fn_name: &str) -> Option<&str> { +#[inline(always)] +fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> { #[cfg(not(feature = "no_object"))] - if fn_name.starts_with(FN_GET) { - return Some(&fn_name[FN_GET.len()..]); + if _fn_name.starts_with(FN_GET) { + return Some(&_fn_name[FN_GET.len()..]); } None } /// Extract the property name from a setter function name. -fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { +#[inline(always)] +fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> { #[cfg(not(feature = "no_object"))] - if fn_name.starts_with(FN_SET) { - return Some(&fn_name[FN_SET.len()..]); + if _fn_name.starts_with(FN_SET) { + return Some(&_fn_name[FN_SET.len()..]); } None @@ -106,17 +116,17 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn call_fn_raw( &self, - scope: &mut Scope, - mods: &mut Imports, + _scope: &mut Scope, + _mods: &mut Imports, state: &mut State, lib: &Module, fn_name: &str, (hash_fn, hash_script): (u64, u64), args: &mut FnCallArgs, is_ref: bool, - is_method: bool, + _is_method: bool, def_val: Option, - level: usize, + _level: usize, ) -> Result<(Dynamic, bool), Box> { self.inc_operations(state)?; @@ -125,7 +135,7 @@ impl Engine { // Check for stack overflow #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] - if level > self.max_call_stack_depth { + if _level > self.limits.max_call_stack_depth { return Err(Box::new( EvalAltResult::ErrorStackOverflow(Position::none()), )); @@ -151,7 +161,7 @@ impl Engine { if let Some(func) = func { #[cfg(not(feature = "no_function"))] - let need_normalize = is_ref && (func.is_pure() || (func.is_script() && !is_method)); + let need_normalize = is_ref && (func.is_pure() || (func.is_script() && !_is_method)); #[cfg(feature = "no_function")] let need_normalize = is_ref && func.is_pure(); @@ -164,25 +174,25 @@ impl Engine { let fn_def = func.get_fn_def(); // Method call of script function - map first argument to `this` - return if is_method { + return if _is_method { let (first, rest) = args.split_at_mut(1); Ok(( self.call_script_fn( - scope, - mods, + _scope, + _mods, state, lib, &mut Some(first[0]), fn_name, fn_def, rest, - level, + _level, )?, false, )) } else { let result = self.call_script_fn( - scope, mods, state, lib, &mut None, fn_name, fn_def, args, level, + _scope, _mods, state, lib, &mut None, fn_name, fn_def, args, _level, )?; // Restore the original reference @@ -256,6 +266,7 @@ impl Engine { } // 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( format!( @@ -268,6 +279,7 @@ impl Engine { } // index setter function not found? + #[cfg(not(feature = "no_index"))] if fn_name == FN_IDX_SET { return Err(Box::new(EvalAltResult::ErrorFunctionNotFound( format!( @@ -305,6 +317,7 @@ impl Engine { /// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// All function arguments not in the first position are always passed by value and thus consumed. /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! + #[cfg(not(feature = "no_function"))] pub(crate) fn call_script_fn( &self, scope: &mut Scope, @@ -486,6 +499,7 @@ impl Engine { } /// Call a dot method. + #[cfg(not(feature = "no_object"))] pub(crate) fn make_method_call( &self, state: &mut State, @@ -506,9 +520,9 @@ impl Engine { // Get a reference to the mutation target Dynamic let obj = target.as_mut(); let mut idx = idx_val.cast::>(); - let mut fn_name = name.as_ref(); + let mut _fn_name = name.as_ref(); - let (result, updated) = if fn_name == KEYWORD_FN_PTR_CALL && obj.is::() { + let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::() { // FnPtr call let fn_ptr = obj.downcast_ref::().unwrap(); let mut curry = fn_ptr.curry().iter().cloned().collect::>(); @@ -527,7 +541,7 @@ impl Engine { self.exec_fn_call( state, lib, fn_name, *native, hash, args, false, false, *def_val, level, ) - } else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::() { + } else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::() { // FnPtr call on object let fn_ptr = idx.remove(0).cast::(); let mut curry = fn_ptr.curry().iter().cloned().collect::>(); @@ -546,7 +560,7 @@ impl Engine { self.exec_fn_call( state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level, ) - } else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() { + } else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() { // Curry call let fn_ptr = obj.downcast_ref::().unwrap(); Ok(( @@ -563,19 +577,20 @@ impl Engine { false, )) } else { + #[cfg(not(feature = "no_object"))] let redirected; - let mut hash = *hash; + let mut _hash = *hash; // Check if it is a map method call in OOP style #[cfg(not(feature = "no_object"))] if let Some(map) = obj.downcast_ref::() { - if let Some(val) = map.get(fn_name) { + if let Some(val) = map.get(_fn_name) { if let Some(f) = val.downcast_ref::() { // Remap the function name redirected = f.get_fn_name().clone(); - fn_name = &redirected; + _fn_name = &redirected; // Recalculate the hash based on the new function name - hash = calc_fn_hash(empty(), fn_name, idx.len(), empty()); + _hash = calc_fn_hash(empty(), _fn_name, idx.len(), empty()); } } }; @@ -585,7 +600,7 @@ impl Engine { let args = arg_values.as_mut(); self.exec_fn_call( - state, lib, fn_name, *native, hash, args, is_ref, true, *def_val, level, + state, lib, _fn_name, *native, _hash, args, is_ref, true, *def_val, level, ) } .map_err(|err| err.new_position(*pos))?; diff --git a/src/fn_native.rs b/src/fn_native.rs index 8fc60714..bfb8b956 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -2,14 +2,18 @@ use crate::any::Dynamic; use crate::engine::Engine; -use crate::module::{FuncReturn, Module}; -use crate::parser::ScriptFnDef; +use crate::module::Module; use crate::result::EvalAltResult; use crate::token::{is_valid_identifier, Position}; -use crate::utils::{ImmutableString, StaticVec}; -use crate::Scope; +use crate::utils::ImmutableString; -use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, mem, string::String, vec::Vec}; +#[cfg(not(feature = "no_function"))] +use crate::{module::FuncReturn, parser::ScriptFnDef, scope::Scope, utils::StaticVec}; + +use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec}; + +#[cfg(not(feature = "no_function"))] +use crate::stdlib::mem; #[cfg(not(feature = "sync"))] use crate::stdlib::rc::Rc; @@ -85,12 +89,17 @@ impl FnPtr { /// Call the function pointer with curried arguments (if any). /// + /// The function must be a script-defined function. It cannot be a Rust function. + /// + /// To call a Rust function, just call it directly in Rust! + /// /// ## WARNING /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. + #[cfg(not(feature = "no_function"))] pub fn call_dynamic( &self, engine: &Engine, diff --git a/src/fn_register.rs b/src/fn_register.rs index 28ee084a..c2eb5534 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -133,12 +133,11 @@ macro_rules! make_func { Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - #[allow(unused_variables, unused_mut)] - let mut drain = args.iter_mut(); + let mut _drain = args.iter_mut(); $( // Downcast every element, panic in case of a type mismatch (which shouldn't happen). // Call the user-supplied function using ($convert) to access it either by value or by reference. - let $par = ($convert)(drain.next().unwrap()); + let $par = ($convert)(_drain.next().unwrap()); )* // Call the function with each parameter value diff --git a/src/lib.rs b/src/lib.rs index 240d87d1..1194fd67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,7 +170,7 @@ pub use parser::{CustomExpr, Expr, FloatWrapper, ReturnType, ScriptFnDef, Stmt}; #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] -pub use engine::{Imports, State as EvalState}; +pub use engine::{Imports, Limits, State as EvalState}; #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] diff --git a/src/module.rs b/src/module.rs index 22632c7d..07430f6e 100644 --- a/src/module.rs +++ b/src/module.rs @@ -2,22 +2,33 @@ use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; -use crate::engine::{make_getter, make_setter, Engine, Imports, FN_IDX_GET, FN_IDX_SET}; -use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync, Shared}; -use crate::parser::{ - FnAccess, - FnAccess::{Private, Public}, - ScriptFnDef, AST, -}; +use crate::engine::Engine; +use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync}; +use crate::parser::{FnAccess, FnAccess::Public}; use crate::result::EvalAltResult; -use crate::scope::{Entry as ScopeEntry, Scope}; use crate::token::{Position, Token}; use crate::utils::{StaticVec, StraightHasherBuilder}; +#[cfg(not(feature = "no_function"))] +use crate::{fn_native::Shared, parser::ScriptFnDef}; + +#[cfg(not(feature = "no_module"))] +use crate::{ + engine::Imports, + parser::AST, + scope::{Entry as ScopeEntry, Scope}, +}; + +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +use crate::engine::{FN_IDX_GET, FN_IDX_SET}; + +#[cfg(not(feature = "no_object"))] +use crate::engine::{make_getter, make_setter}; + use crate::stdlib::{ any::TypeId, boxed::Box, - cell::RefCell, collections::HashMap, fmt, format, iter::empty, @@ -25,11 +36,16 @@ use crate::stdlib::{ num::NonZeroUsize, ops::{Deref, DerefMut}, string::{String, ToString}, - vec, vec::Vec, }; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_module"))] +#[cfg(not(feature = "sync"))] +use crate::stdlib::cell::RefCell; + +#[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_module"))] #[cfg(feature = "sync")] use crate::stdlib::sync::RwLock; @@ -738,6 +754,8 @@ impl Module { /// }); /// assert!(module.contains_fn(hash)); /// ``` + #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "no_index"))] pub fn set_indexer_set_fn( &mut self, func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static, @@ -781,6 +799,8 @@ impl Module { /// assert!(module.contains_fn(hash_get)); /// assert!(module.contains_fn(hash_set)); /// ``` + #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "no_index"))] pub fn set_indexer_get_set_fn( &mut self, getter: impl Fn(&mut A, B) -> FuncReturn + SendSync + 'static, @@ -909,11 +929,11 @@ impl Module { self.merge_filtered(other, |_, _, _| true) } - /// Merge another module into this module, with only selected functions based on a filter predicate. + /// Merge another module into this module, with only selected script-defined functions based on a filter predicate. pub(crate) fn merge_filtered( &mut self, other: &Self, - filter: impl Fn(FnAccess, &str, usize) -> bool, + _filter: impl Fn(FnAccess, &str, usize) -> bool, ) -> &mut Self { self.variables .extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone()))); @@ -924,7 +944,7 @@ impl Module { .iter() .filter(|(_, (_, _, _, v))| match v { #[cfg(not(feature = "no_function"))] - Func::Script(ref f) => filter(f.access, f.name.as_str(), f.params.len()), + Func::Script(ref f) => _filter(f.access, f.name.as_str(), f.params.len()), _ => true, }) .map(|(&k, v)| (k, v.clone())), @@ -975,6 +995,7 @@ 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)> { @@ -1038,6 +1059,7 @@ impl Module { /// Scan through all the sub-modules in the module build an index of all /// variables and external Rust functions via hashing. + #[cfg(not(feature = "no_module"))] pub(crate) fn index_all_sub_modules(&mut self) { // Collect a particular module. fn index_module<'a>( @@ -1063,8 +1085,8 @@ impl Module { for (name, access, params, func) in module.functions.values() { match access { // Private functions are not exported - Private => continue, - Public => (), + FnAccess::Private => continue, + FnAccess::Public => (), } #[cfg(not(feature = "no_function"))] @@ -1100,10 +1122,13 @@ impl Module { return; } - let mut variables = Vec::new(); - let mut functions = Vec::new(); + let mut qualifiers: Vec<_> = Default::default(); + let mut variables: Vec<_> = Default::default(); + let mut functions: Vec<_> = Default::default(); - index_module(self, &mut vec!["root"], &mut variables, &mut functions); + qualifiers.push("root"); + + index_module(self, &mut qualifiers, &mut variables, &mut functions); self.all_variables = variables.into_iter().collect(); self.all_functions = functions.into_iter().collect(); diff --git a/src/optimize.rs b/src/optimize.rs index 738ea300..0948fbd5 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -6,11 +6,14 @@ use crate::engine::{ Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::module::Module; -use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST}; +use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::token::is_valid_identifier; use crate::utils::StaticVec; +#[cfg(not(feature = "no_function"))] +use crate::parser::ReturnType; + #[cfg(feature = "internals")] use crate::parser::CustomExpr; @@ -249,6 +252,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { // let id; stmt @ Stmt::Let(_) => stmt, // import expr as id; + #[cfg(not(feature = "no_module"))] Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))), // { block } Stmt::Block(x) => { @@ -290,6 +294,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { match expr { Stmt::Let(x) if x.1.is_none() => removed = true, Stmt::Let(x) if x.1.is_some() => removed = x.1.unwrap().is_pure(), + #[cfg(not(feature = "no_module"))] Stmt::Import(x) => removed = x.0.is_pure(), _ => { result.push(expr); @@ -345,8 +350,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { state.set_dirty(); Stmt::Noop(pos) } - // Only one let/import statement - leave it alone - [Stmt::Let(_)] | [Stmt::Import(_)] => Stmt::Block(Box::new((result.into(), pos))), + // Only one let statement - leave it alone + [Stmt::Let(_)] => Stmt::Block(Box::new((result.into(), pos))), + // Only one import statement - leave it alone + #[cfg(not(feature = "no_module"))] + [Stmt::Import(_)] => Stmt::Block(Box::new((result.into(), pos))), // Only one statement - promote [_] => { state.set_dirty(); @@ -557,16 +565,16 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { // First search in functions lib (can override built-in) // Cater for both normal function call style and method call style (one additional arguments) #[cfg(not(feature = "no_function"))] - let has_script_fn = state.lib.iter_fn().find(|(_, _, _, f)| { + let _has_script_fn = state.lib.iter_fn().find(|(_, _, _, f)| { if !f.is_script() { return false; } let fn_def = f.get_fn_def(); fn_def.name.as_str() == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len()) }).is_some(); #[cfg(feature = "no_function")] - const has_script_fn: bool = false; + let _has_script_fn: bool = false; - if has_script_fn { + if _has_script_fn { // A script-defined function overrides the built-in function - do not make the call x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); return Expr::FnCall(x); @@ -686,7 +694,9 @@ fn optimize( // Keep all variable declarations at this level // and always keep the last return value let keep = match stmt { - Stmt::Let(_) | Stmt::Import(_) => true, + Stmt::Let(_) => true, + #[cfg(not(feature = "no_module"))] + Stmt::Import(_) => true, _ => i == num_statements - 1, }; optimize_stmt(stmt, &mut state, keep) @@ -721,7 +731,7 @@ pub fn optimize_into_ast( engine: &Engine, scope: &Scope, statements: Vec, - functions: Vec, + _functions: Vec, level: OptimizationLevel, ) -> AST { #[cfg(feature = "no_optimize")] @@ -735,7 +745,7 @@ pub fn optimize_into_ast( // We only need the script library's signatures for optimization purposes let mut lib2 = Module::new(); - functions + _functions .iter() .map(|fn_def| { ScriptFnDef { @@ -751,7 +761,7 @@ pub fn optimize_into_ast( lib2.set_script_fn(fn_def); }); - functions + _functions .into_iter() .map(|mut fn_def| { let pos = fn_def.body.position(); @@ -782,7 +792,7 @@ pub fn optimize_into_ast( module.set_script_fn(fn_def); }); } else { - functions.into_iter().for_each(|fn_def| { + _functions.into_iter().for_each(|fn_def| { module.set_script_fn(fn_def); }); } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 3e3852d6..61350996 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -1,8 +1,9 @@ use crate::def_package; use crate::module::FuncReturn; use crate::parser::INT; -use crate::result::EvalAltResult; -use crate::token::Position; + +#[cfg(not(feature = "unchecked"))] +use crate::{result::EvalAltResult, token::Position}; #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; @@ -11,22 +12,25 @@ use crate::parser::FLOAT; #[cfg(feature = "no_std")] use num_traits::*; +#[cfg(not(feature = "unchecked"))] use num_traits::{ identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, }; -use crate::stdlib::{ - boxed::Box, - fmt::Display, - format, - ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Sub}, -}; +use crate::stdlib::ops::{BitAnd, BitOr, BitXor}; + +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] +use crate::stdlib::ops::{Add, Div, Mul, Neg, Rem, Sub}; #[cfg(feature = "unchecked")] use crate::stdlib::ops::{Shl, Shr}; +#[cfg(not(feature = "unchecked"))] +use crate::stdlib::{boxed::Box, fmt::Display, format}; + // Checked add +#[cfg(not(feature = "unchecked"))] pub(crate) fn add(x: T, y: T) -> FuncReturn { x.checked_add(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( @@ -36,6 +40,7 @@ pub(crate) fn add(x: T, y: T) -> FuncReturn { }) } // Checked subtract +#[cfg(not(feature = "unchecked"))] pub(crate) fn sub(x: T, y: T) -> FuncReturn { x.checked_sub(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( @@ -45,6 +50,7 @@ pub(crate) fn sub(x: T, y: T) -> FuncReturn { }) } // Checked multiply +#[cfg(not(feature = "unchecked"))] pub(crate) fn mul(x: T, y: T) -> FuncReturn { x.checked_mul(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( @@ -54,6 +60,7 @@ pub(crate) fn mul(x: T, y: T) -> FuncReturn { }) } // Checked divide +#[cfg(not(feature = "unchecked"))] pub(crate) fn div(x: T, y: T) -> FuncReturn where T: Display + CheckedDiv + PartialEq + Zero, @@ -74,6 +81,7 @@ where }) } // Checked negative - e.g. -(i32::MIN) will overflow i32::MAX +#[cfg(not(feature = "unchecked"))] pub(crate) fn neg(x: T) -> FuncReturn { x.checked_neg().ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( @@ -83,6 +91,7 @@ pub(crate) fn neg(x: T) -> FuncReturn { }) } // Checked absolute +#[cfg(not(feature = "unchecked"))] pub(crate) fn abs(x: T) -> FuncReturn { // FIX - We don't use Signed::abs() here because, contrary to documentation, it panics // when the number is ::MIN instead of returning ::MIN itself. @@ -98,26 +107,32 @@ pub(crate) fn abs(x: T) -> FuncRetu } } // Unchecked add - may panic on overflow +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn add_u(x: T, y: T) -> FuncReturn<::Output> { Ok(x + y) } // Unchecked subtract - may panic on underflow +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn sub_u(x: T, y: T) -> FuncReturn<::Output> { Ok(x - y) } // Unchecked multiply - may panic on overflow +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn mul_u(x: T, y: T) -> FuncReturn<::Output> { Ok(x * y) } // Unchecked divide - may panic when dividing by zero +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn div_u(x: T, y: T) -> FuncReturn<::Output> { Ok(x / y) } // Unchecked negative - may panic on overflow +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn neg_u(x: T) -> FuncReturn<::Output> { Ok(-x) } // Unchecked absolute - may panic on overflow +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn abs_u(x: T) -> FuncReturn<::Output> where T: Neg + PartialOrd + Default + Into<::Output>, @@ -140,6 +155,7 @@ fn binary_xor(x: T, y: T) -> FuncReturn<::Output> { Ok(x ^ y) } // Checked left-shift +#[cfg(not(feature = "unchecked"))] pub(crate) fn shl(x: T, y: INT) -> FuncReturn { // Cannot shift by a negative number of bits if y < 0 { @@ -157,6 +173,7 @@ pub(crate) fn shl(x: T, y: INT) -> FuncReturn { }) } // Checked right-shift +#[cfg(not(feature = "unchecked"))] pub(crate) fn shr(x: T, y: INT) -> FuncReturn { // Cannot shift by a negative number of bits if y < 0 { @@ -184,6 +201,7 @@ pub(crate) fn shr_u>(x: T, y: T) -> FuncReturn<>::Output> Ok(x.shr(y)) } // Checked modulo +#[cfg(not(feature = "unchecked"))] pub(crate) fn modulo(x: T, y: T) -> FuncReturn { x.checked_rem(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( @@ -193,10 +211,12 @@ pub(crate) fn modulo(x: T, y: T) -> FuncReturn { }) } // Unchecked modulo - may panic if dividing by zero +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn modulo_u(x: T, y: T) -> FuncReturn<::Output> { Ok(x % y) } // Checked power +#[cfg(not(feature = "unchecked"))] pub(crate) fn pow_i_i(x: INT, y: INT) -> FuncReturn { #[cfg(not(feature = "only_i32"))] if y > (u32::MAX as INT) { @@ -245,6 +265,7 @@ pub(crate) fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn { } // Checked power #[cfg(not(feature = "no_float"))] +#[cfg(not(feature = "unchecked"))] pub(crate) fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { // Raise to power that is larger than an i32 if y > (i32::MAX as INT) { diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 0a580933..c0bea7ee 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -5,10 +5,14 @@ use crate::def_package; use crate::engine::{Array, Engine}; use crate::module::{FuncReturn, Module}; use crate::parser::{ImmutableString, INT}; -use crate::result::EvalAltResult; -use crate::token::Position; -use crate::stdlib::{any::TypeId, boxed::Box, string::ToString}; +#[cfg(not(feature = "unchecked"))] +use crate::{result::EvalAltResult, token::Position}; + +use crate::stdlib::{any::TypeId, boxed::Box}; + +#[cfg(not(feature = "unchecked"))] +use crate::stdlib::string::ToString; // Register array utility functions fn push(list: &mut Array, item: T) -> FuncReturn<()> { @@ -26,7 +30,7 @@ fn ins(list: &mut Array, position: INT, item: T) -> FuncRetu Ok(()) } fn pad( - engine: &Engine, + _engine: &Engine, _: &Module, args: &mut [&mut Dynamic], ) -> FuncReturn<()> { @@ -34,10 +38,13 @@ fn pad( // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - if engine.max_array_size > 0 && len > 0 && (len as usize) > engine.max_array_size { + if _engine.limits.max_array_size > 0 + && len > 0 + && (len as usize) > _engine.limits.max_array_size + { return Err(Box::new(EvalAltResult::ErrorDataTooLarge( "Size of array".to_string(), - engine.max_array_size, + _engine.limits.max_array_size, len as usize, Position::none(), ))); diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index a86e4665..cc1aa000 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -1,16 +1,20 @@ #![cfg(not(feature = "no_object"))] -use crate::any::Dynamic; use crate::def_package; use crate::engine::Map; -use crate::module::FuncReturn; use crate::parser::{ImmutableString, INT}; +#[cfg(not(feature = "no_index"))] +use crate::{any::Dynamic, module::FuncReturn}; + +#[cfg(not(feature = "no_index"))] use crate::stdlib::vec::Vec; +#[cfg(not(feature = "no_index"))] fn map_get_keys(map: &mut Map) -> FuncReturn> { Ok(map.iter().map(|(k, _)| k.clone().into()).collect()) } +#[cfg(not(feature = "no_index"))] fn map_get_values(map: &mut Map) -> FuncReturn> { Ok(map.iter().map(|(_, v)| v.clone()).collect()) } diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index aed7e5b7..55bb68a3 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,20 +1,26 @@ use crate::def_package; use crate::parser::INT; -use crate::result::EvalAltResult; -use crate::token::Position; #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; +#[cfg(not(feature = "no_float"))] +#[cfg(not(feature = "unchecked"))] +use crate::{result::EvalAltResult, token::Position}; + #[cfg(not(feature = "no_float"))] #[cfg(feature = "no_std")] use num_traits::*; -use crate::stdlib::{boxed::Box, format, i32, i64}; +#[cfg(not(feature = "no_float"))] +#[cfg(not(feature = "unchecked"))] +use crate::stdlib::{boxed::Box, format}; #[cfg(feature = "only_i32")] +#[cfg(not(feature = "unchecked"))] pub const MAX_INT: INT = i32::MAX; #[cfg(not(feature = "only_i32"))] +#[cfg(not(feature = "unchecked"))] pub const MAX_INT: INT = i64::MAX; def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 8207a966..ccd78add 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -24,7 +24,6 @@ pub use arithmetic::ArithmeticPackage; #[cfg(not(feature = "no_index"))] pub use array_basic::BasicArrayPackage; pub use eval::EvalPackage; -#[cfg(not(feature = "no_function"))] pub use fn_basic::BasicFnPackage; pub use iter_basic::BasicIteratorPackage; pub use logic::LogicPackage; diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 248b0e93..62a5fdb2 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -3,10 +3,11 @@ use crate::def_package; use crate::engine::Engine; use crate::module::{FuncReturn, Module}; use crate::parser::{ImmutableString, INT}; -use crate::result::EvalAltResult; -use crate::token::Position; use crate::utils::StaticVec; +#[cfg(not(feature = "unchecked"))] +use crate::{result::EvalAltResult, token::Position}; + #[cfg(not(feature = "no_index"))] use crate::engine::Array; @@ -226,15 +227,15 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str lib.set_raw_fn( "pad", &[TypeId::of::(), TypeId::of::(), TypeId::of::()], - |engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| { + |_engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| { let len = *args[1].downcast_ref::< INT>().unwrap(); // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] - if engine.max_string_size > 0 && len > 0 && (len as usize) > engine.max_string_size { + if _engine.limits.max_string_size > 0 && len > 0 && (len as usize) > _engine.limits.max_string_size { return Err(Box::new(EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - engine.max_string_size, + _engine.limits.max_string_size, len as usize, Position::none(), ))); @@ -253,10 +254,11 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str p.push(ch); } - if engine.max_string_size > 0 && s.len() > engine.max_string_size { + #[cfg(not(feature = "unchecked"))] + if _engine.limits.max_string_size > 0 && s.len() > _engine.limits.max_string_size { return Err(Box::new(EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - engine.max_string_size, + _engine.limits.max_string_size, s.len(), Position::none(), ))); diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 7c166ab1..d554b68e 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -2,6 +2,7 @@ use super::logic::{eq, gt, gte, lt, lte, ne}; #[cfg(feature = "no_float")] +#[cfg(not(feature = "unchecked"))] use super::math_basic::MAX_INT; use crate::def_package; @@ -11,7 +12,11 @@ use crate::result::EvalAltResult; use crate::parser::FLOAT; #[cfg(feature = "no_float")] -use crate::{module::FuncReturn, parser::INT, token::Position}; +use crate::parser::INT; + +#[cfg(feature = "no_float")] +#[cfg(not(feature = "unchecked"))] +use crate::token::Position; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::time::Instant; diff --git a/src/parser.rs b/src/parser.rs index da0409db..baf1be85 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,10 +2,7 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; -use crate::engine::{ - make_getter, make_setter, Engine, FN_ANONYMOUS, 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::Shared; use crate::module::{Module, ModuleRef}; @@ -15,6 +12,12 @@ use crate::syntax::FnCustomSyntaxEval; use crate::token::{is_valid_identifier, Position, Token, TokenStream}; use crate::utils::{StaticVec, StraightHasherBuilder}; +#[cfg(not(feature = "no_function"))] +use crate::engine::FN_ANONYMOUS; + +#[cfg(not(feature = "no_object"))] +use crate::engine::{make_getter, make_setter}; + use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -32,9 +35,11 @@ use crate::stdlib::{ }; #[cfg(not(feature = "no_std"))] +#[cfg(not(feature = "no_function"))] use crate::stdlib::collections::hash_map::DefaultHasher; #[cfg(feature = "no_std")] +#[cfg(not(feature = "no_function"))] use ahash::AHasher; /// The system integer type. @@ -84,7 +89,7 @@ impl AST { &self.0 } - /// Get the statements. + /// [INTERNALS] Get the statements. #[cfg(feature = "internals")] #[deprecated(note = "this method is volatile and may change")] pub fn statements(&self) -> &[Stmt] { @@ -102,7 +107,7 @@ impl AST { &self.1 } - /// Get the internal `Module` containing all script-defined functions. + /// [INTERNALS] Get the internal `Module` containing all script-defined functions. #[cfg(feature = "internals")] #[deprecated(note = "this method is volatile and may change")] pub fn lib(&self) -> &Module { @@ -404,20 +409,28 @@ struct ParseState<'e> { /// Encapsulates a local stack with variable names to simulate an actual runtime scope. modules: Vec, /// Maximum levels of expression nesting. + #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, /// Maximum levels of expression nesting in functions. + #[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize, } impl<'e> ParseState<'e> { /// Create a new `ParseState`. - pub fn new(engine: &'e Engine, max_expr_depth: usize, max_function_expr_depth: usize) -> Self { + pub fn new( + engine: &'e Engine, + #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, + #[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize, + ) -> Self { Self { engine, - max_expr_depth, - max_function_expr_depth, stack: Default::default(), modules: Default::default(), + #[cfg(not(feature = "unchecked"))] + max_expr_depth, + #[cfg(not(feature = "unchecked"))] + max_function_expr_depth, } } /// Find a variable by name in the `ParseState`, searching in reverse. @@ -476,6 +489,7 @@ impl ParseSettings { } } /// Make sure that the current level of expression nesting is within the maximum limit. + #[cfg(not(feature = "unchecked"))] pub fn ensure_level_within_max_limit(&self, limit: usize) -> Result<(), ParseError> { if limit == 0 { Ok(()) @@ -519,8 +533,10 @@ pub enum Stmt { /// return/throw ReturnWithVal(Box<((ReturnType, Position), Option)>), /// import expr as module + #[cfg(not(feature = "no_module"))] Import(Box<(Expr, (String, Position))>), /// expr id as name, ... + #[cfg(not(feature = "no_module"))] Export(Box)>>), } @@ -544,7 +560,10 @@ impl Stmt { Stmt::While(x) => x.1.position(), Stmt::Loop(x) => x.position(), Stmt::For(x) => x.2.position(), + + #[cfg(not(feature = "no_module"))] Stmt::Import(x) => (x.1).1, + #[cfg(not(feature = "no_module"))] Stmt::Export(x) => (x.get(0).0).1, } } @@ -563,12 +582,13 @@ impl Stmt { Stmt::Let(_) | Stmt::Const(_) - | Stmt::Import(_) - | Stmt::Export(_) | Stmt::Expr(_) | Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_) => false, + + #[cfg(not(feature = "no_module"))] + Stmt::Import(_) | Stmt::Export(_) => false, } } @@ -587,7 +607,10 @@ impl Stmt { Stmt::Let(_) | Stmt::Const(_) => false, Stmt::Block(x) => x.0.iter().all(Stmt::is_pure), Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_) => false, + + #[cfg(not(feature = "no_module"))] Stmt::Import(_) => false, + #[cfg(not(feature = "no_module"))] Stmt::Export(_) => false, } } @@ -950,6 +973,7 @@ impl Expr { } /// Convert a `Variable` into a `Property`. All other variants are untouched. + #[cfg(not(feature = "no_object"))] pub(crate) fn into_property(self) -> Self { match self { Self::Variable(x) if x.1.is_none() => { @@ -996,6 +1020,7 @@ fn parse_paren_expr( lib: &mut FunctionsLib, settings: ParseSettings, ) -> Result { + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; if match_token(input, Token::RightParen)? { @@ -1028,6 +1053,8 @@ fn parse_call_expr( settings: ParseSettings, ) -> Result { let (token, token_pos) = input.peek().unwrap(); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut args = StaticVec::new(); @@ -1141,6 +1168,7 @@ fn parse_call_expr( /// Parse an indexing chain. /// Indexing binds to the right, so this call parses all possible levels of indexing following in the input. +#[cfg(not(feature = "no_index"))] fn parse_index_chain( input: &mut TokenStream, state: &mut ParseState, @@ -1148,6 +1176,7 @@ fn parse_index_chain( lhs: Expr, mut settings: ParseSettings, ) -> Result { + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let idx_expr = parse_expr(input, state, lib, settings.level_up())?; @@ -1321,21 +1350,25 @@ fn parse_index_chain( } /// Parse an array literal. +#[cfg(not(feature = "no_index"))] fn parse_array_literal( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, settings: ParseSettings, ) -> Result { + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut arr = StaticVec::new(); while !input.peek().unwrap().0.is_eof() { - if state.engine.max_array_size > 0 && arr.len() >= state.engine.max_array_size { + #[cfg(not(feature = "unchecked"))] + if state.engine.limits.max_array_size > 0 && arr.len() >= state.engine.limits.max_array_size + { return Err(PERR::LiteralTooLarge( "Size of array literal".to_string(), - state.engine.max_array_size, + state.engine.limits.max_array_size, ) .into_err(input.peek().unwrap().1)); } @@ -1378,12 +1411,14 @@ fn parse_array_literal( } /// Parse a map literal. +#[cfg(not(feature = "no_object"))] fn parse_map_literal( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, settings: ParseSettings, ) -> Result { + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut map = StaticVec::new(); @@ -1436,10 +1471,11 @@ fn parse_map_literal( } }; - if state.engine.max_map_size > 0 && map.len() >= state.engine.max_map_size { + #[cfg(not(feature = "unchecked"))] + if state.engine.limits.max_map_size > 0 && map.len() >= state.engine.limits.max_map_size { return Err(PERR::LiteralTooLarge( "Number of properties in object map literal".to_string(), - state.engine.max_map_size, + state.engine.limits.max_map_size, ) .into_err(input.peek().unwrap().1)); } @@ -1492,6 +1528,8 @@ fn parse_primary( ) -> Result { let (token, token_pos) = input.peek().unwrap(); settings.pos = *token_pos; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let (token, _) = match token { @@ -1626,6 +1664,8 @@ fn parse_unary( ) -> Result { let (token, token_pos) = input.peek().unwrap(); settings.pos = *token_pos; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; match token { @@ -1708,7 +1748,9 @@ fn parse_unary( Token::Pipe | Token::Or => { let mut state = ParseState::new( state.engine, + #[cfg(not(feature = "unchecked"))] state.max_function_expr_depth, + #[cfg(not(feature = "unchecked"))] state.max_function_expr_depth, ); @@ -1809,6 +1851,8 @@ fn parse_op_assignment_stmt( ) -> Result { let (token, token_pos) = input.peek().unwrap(); settings.pos = *token_pos; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let op = match token { @@ -1835,6 +1879,7 @@ fn parse_op_assignment_stmt( } /// Make a dot expression. +#[cfg(not(feature = "no_object"))] fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { Ok(match (lhs, rhs) { // idx_lhs[idx_expr].rhs @@ -2048,6 +2093,8 @@ fn parse_binary_op( mut settings: ParseSettings, ) -> Result { settings.pos = lhs.position(); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut root = lhs; @@ -2081,6 +2128,8 @@ fn parse_binary_op( settings = settings.level_up(); settings.pos = pos; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let cmp_def = Some(false); @@ -2164,6 +2213,8 @@ fn parse_expr( mut settings: ParseSettings, ) -> Result { settings.pos = input.peek().unwrap().1; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // Check if it is a custom syntax. @@ -2291,6 +2342,8 @@ fn parse_if( ) -> Result { // if ... settings.pos = eat_token(input, Token::If); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // if guard { if_body } @@ -2324,6 +2377,8 @@ fn parse_while( ) -> Result { // while ... settings.pos = eat_token(input, Token::While); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // while guard { body } @@ -2346,6 +2401,8 @@ fn parse_loop( ) -> Result { // loop ... settings.pos = eat_token(input, Token::Loop); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // loop { body } @@ -2364,6 +2421,8 @@ fn parse_for( ) -> Result { // for ... settings.pos = eat_token(input, Token::For); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // for name ... @@ -2417,6 +2476,8 @@ fn parse_let( ) -> Result { // let/const... (specified in `var_type`) settings.pos = input.next().unwrap().1; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // let name ... @@ -2475,6 +2536,8 @@ fn parse_import( ) -> Result { // import ... settings.pos = eat_token(input, Token::Import); + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; // import expr ... @@ -2509,12 +2572,14 @@ fn parse_import( #[cfg(not(feature = "no_module"))] fn parse_export( input: &mut TokenStream, - state: &mut ParseState, + _state: &mut ParseState, _lib: &mut FunctionsLib, mut settings: ParseSettings, ) -> Result { settings.pos = eat_token(input, Token::Export); - settings.ensure_level_within_max_limit(state.max_expr_depth)?; + + #[cfg(not(feature = "unchecked"))] + settings.ensure_level_within_max_limit(_state.max_expr_depth)?; let mut exports = StaticVec::new(); @@ -2594,6 +2659,7 @@ fn parse_block( } }; + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut statements = StaticVec::new(); @@ -2657,6 +2723,8 @@ fn parse_expr_stmt( mut settings: ParseSettings, ) -> Result { settings.pos = input.peek().unwrap().1; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let expr = parse_expr(input, state, lib, settings.level_up())?; @@ -2678,6 +2746,8 @@ fn parse_stmt( x => x, }; settings.pos = *token_pos; + + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; match token { @@ -2703,7 +2773,9 @@ fn parse_stmt( (Token::Fn, pos) => { let mut state = ParseState::new( state.engine, + #[cfg(not(feature = "unchecked"))] state.max_function_expr_depth, + #[cfg(not(feature = "unchecked"))] state.max_function_expr_depth, ); @@ -2807,6 +2879,7 @@ fn parse_fn( access: FnAccess, mut settings: ParseSettings, ) -> Result { + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let name = match input.next().unwrap() { @@ -2899,6 +2972,7 @@ fn parse_anon_fn( lib: &mut FunctionsLib, mut settings: ParseSettings, ) -> Result<(Expr, ScriptFnDef), ParseError> { + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut params = Vec::new(); @@ -2994,7 +3068,15 @@ impl Engine { optimization_level: OptimizationLevel, ) -> Result { let mut functions = Default::default(); - let mut state = ParseState::new(self, self.max_expr_depth, self.max_function_expr_depth); + + let mut state = ParseState::new( + self, + #[cfg(not(feature = "unchecked"))] + self.limits.max_expr_depth, + #[cfg(not(feature = "unchecked"))] + self.limits.max_function_expr_depth, + ); + let settings = ParseSettings { allow_if_expr: false, allow_stmt_expr: false, @@ -3032,7 +3114,14 @@ impl Engine { ) -> Result<(Vec, Vec), ParseError> { let mut statements: Vec = Default::default(); let mut functions = Default::default(); - let mut state = ParseState::new(self, self.max_expr_depth, self.max_function_expr_depth); + + let mut state = ParseState::new( + self, + #[cfg(not(feature = "unchecked"))] + self.limits.max_expr_depth, + #[cfg(not(feature = "unchecked"))] + self.limits.max_function_expr_depth, + ); while !input.peek().unwrap().0.is_eof() { let settings = ParseSettings { diff --git a/src/result.rs b/src/result.rs index 7dd3d6fa..66fd9cdf 100644 --- a/src/result.rs +++ b/src/result.rs @@ -34,10 +34,10 @@ pub enum EvalAltResult { #[cfg(not(target_arch = "wasm32"))] ErrorReadingScriptFile(PathBuf, Position, std::io::Error), - /// Call to an unknown function. Wrapped value is the name of the function. + /// Call to an unknown function. Wrapped value is the signature of the function. ErrorFunctionNotFound(String, Position), /// An error has occurred inside a called function. - /// Wrapped values re the name of the function and the interior error. + /// Wrapped values are the name of the function and the interior error. ErrorInFunctionCall(String, Box, Position), /// Access to `this` that is not bounded. ErrorUnboundedThis(Position), diff --git a/src/scope.rs b/src/scope.rs index 8342c792..5693554c 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -377,6 +377,7 @@ impl<'a> Scope<'a> { } /// Update the access type of an entry in the Scope. + #[cfg(not(feature = "no_module"))] pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self { let entry = self.0.get_mut(index).expect("invalid index in Scope"); entry.alias = Some(Box::new(alias)); @@ -384,6 +385,7 @@ impl<'a> Scope<'a> { } /// Get an iterator to entries in the Scope. + #[cfg(not(feature = "no_module"))] pub(crate) fn into_iter(self) -> impl Iterator> { self.0.into_iter() } diff --git a/src/serde/de.rs b/src/serde/de.rs index 4ab9919a..8a60604c 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -8,17 +8,20 @@ use crate::token::Position; use crate::utils::ImmutableString; use serde::de::{ - DeserializeSeed, Deserializer, EnumAccess, Error, IntoDeserializer, MapAccess, SeqAccess, - VariantAccess, Visitor, + DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor, }; use serde::Deserialize; #[cfg(not(feature = "no_index"))] use crate::engine::Array; + #[cfg(not(feature = "no_object"))] use crate::engine::Map; -use crate::stdlib::{any::type_name, fmt}; +#[cfg(not(feature = "no_object"))] +use serde::de::{EnumAccess, VariantAccess}; + +use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] @@ -46,8 +49,12 @@ impl<'de> DynamicDeserializer<'de> { } /// Shortcut for a type conversion error. fn type_error(&self) -> Result> { + self.type_error_str(type_name::()) + } + /// Shortcut for a type conversion error. + fn type_error_str(&self, error: &str) -> Result> { Err(Box::new(EvalAltResult::ErrorMismatchOutputType( - type_name::().into(), + error.into(), self.value.type_name().into(), Position::none(), ))) @@ -283,23 +290,23 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_f32>(self, visitor: V) -> Result> { + fn deserialize_f32>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_float"))] return self .value .downcast_ref::() - .map_or_else(|| self.type_error(), |&x| visitor.visit_f32(x)); + .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); #[cfg(feature = "no_float")] return self.type_error_str("f32"); } - fn deserialize_f64>(self, visitor: V) -> Result> { + fn deserialize_f64>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_float"))] return self .value .downcast_ref::() - .map_or_else(|| self.type_error(), |&x| visitor.visit_f64(x)); + .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); #[cfg(feature = "no_float")] return self.type_error_str("f64"); @@ -359,11 +366,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { visitor.visit_newtype_struct(self) } - fn deserialize_seq>(self, visitor: V) -> Result> { + fn deserialize_seq>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_index"))] return self.value.downcast_ref::().map_or_else( || self.type_error(), - |arr| visitor.visit_seq(IterateArray::new(arr.iter())), + |arr| _visitor.visit_seq(IterateArray::new(arr.iter())), ); #[cfg(feature = "no_index")] @@ -387,11 +394,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_seq(visitor) } - fn deserialize_map>(self, visitor: V) -> Result> { + fn deserialize_map>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_object"))] return self.value.downcast_ref::().map_or_else( || self.type_error(), - |map| visitor.visit_map(IterateMap::new(map.keys(), map.values())), + |map| _visitor.visit_map(IterateMap::new(map.keys(), map.values())), ); #[cfg(feature = "no_object")] @@ -461,6 +468,7 @@ where iter: ITER, } +#[cfg(not(feature = "no_index"))] impl<'a, ITER> IterateArray<'a, ITER> where ITER: Iterator, @@ -502,6 +510,7 @@ where values: VALUES, } +#[cfg(not(feature = "no_object"))] impl<'a, KEYS, VALUES> IterateMap<'a, KEYS, VALUES> where KEYS: Iterator, diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 7c3f9321..c11c7f1c 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -10,27 +10,36 @@ use crate::engine::Array; use crate::engine::Map; use serde::ser::{ - Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, - SerializeTupleStruct, SerializeTupleVariant, Serializer, + Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, + Serializer, }; use serde::Serialize; -use crate::stdlib::{any::type_name, fmt, mem}; +#[cfg(not(any(feature = "no_object", feature = "no_index")))] +use serde::ser::SerializeTupleVariant; + +#[cfg(not(feature = "no_object"))] +use serde::ser::SerializeStructVariant; + +use crate::stdlib::{boxed::Box, fmt, string::ToString}; + +#[cfg(not(feature = "no_object"))] +use crate::stdlib::mem; /// Serializer for `Dynamic` which is kept as a reference. pub struct DynamicSerializer { /// Buffer to hold a temporary key. - key: Dynamic, + _key: Dynamic, /// Buffer to hold a temporary value. - value: Dynamic, + _value: Dynamic, } impl DynamicSerializer { /// Create a `DynamicSerializer` from a `Dynamic` value. - pub fn new(value: Dynamic) -> Self { + pub fn new(_value: Dynamic) -> Self { Self { - key: Default::default(), - value, + _key: Default::default(), + _value, } } } @@ -280,13 +289,13 @@ impl Serializer for &mut DynamicSerializer { self, _name: &'static str, _variant_index: u32, - variant: &'static str, - value: &T, + _variant: &'static str, + _value: &T, ) -> Result> { #[cfg(not(feature = "no_object"))] { - let content = to_dynamic(value)?; - make_variant(variant, content) + let content = to_dynamic(_value)?; + make_variant(_variant, content) } #[cfg(feature = "no_object")] return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( @@ -323,13 +332,13 @@ impl Serializer for &mut DynamicSerializer { self, _name: &'static str, _variant_index: u32, - variant: &'static str, - len: usize, + _variant: &'static str, + _len: usize, ) -> Result> { #[cfg(not(any(feature = "no_object", feature = "no_index")))] return Ok(TupleVariantSerializer { - variant, - array: Array::with_capacity(len), + variant: _variant, + array: Array::with_capacity(_len), }); #[cfg(any(feature = "no_object", feature = "no_index"))] { @@ -368,13 +377,13 @@ impl Serializer for &mut DynamicSerializer { self, _name: &'static str, _variant_index: u32, - variant: &'static str, - len: usize, + _variant: &'static str, + _len: usize, ) -> Result> { #[cfg(not(feature = "no_object"))] return Ok(StructVariantSerializer { - variant, - map: Map::with_capacity(len), + variant: _variant, + map: Map::with_capacity(_len), }); #[cfg(feature = "no_object")] return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( @@ -391,13 +400,13 @@ impl SerializeSeq for DynamicSerializer { fn serialize_element( &mut self, - value: &T, + _value: &T, ) -> Result<(), Box> { #[cfg(not(feature = "no_index"))] { - let value = value.serialize(&mut *self)?; - let arr = self.value.downcast_mut::().unwrap(); - arr.push(value); + let _value = _value.serialize(&mut *self)?; + let arr = self._value.downcast_mut::().unwrap(); + arr.push(_value); Ok(()) } #[cfg(feature = "no_index")] @@ -407,7 +416,7 @@ impl SerializeSeq for DynamicSerializer { // Close the sequence. fn end(self) -> Result> { #[cfg(not(feature = "no_index"))] - return Ok(self.value); + return Ok(self._value); #[cfg(feature = "no_index")] unreachable!() } @@ -419,13 +428,13 @@ impl SerializeTuple for DynamicSerializer { fn serialize_element( &mut self, - value: &T, + _value: &T, ) -> Result<(), Box> { #[cfg(not(feature = "no_index"))] { - let value = value.serialize(&mut *self)?; - let arr = self.value.downcast_mut::().unwrap(); - arr.push(value); + let _value = _value.serialize(&mut *self)?; + let arr = self._value.downcast_mut::().unwrap(); + arr.push(_value); Ok(()) } #[cfg(feature = "no_index")] @@ -434,7 +443,7 @@ impl SerializeTuple for DynamicSerializer { fn end(self) -> Result> { #[cfg(not(feature = "no_index"))] - return Ok(self.value); + return Ok(self._value); #[cfg(feature = "no_index")] unreachable!() } @@ -446,13 +455,13 @@ impl SerializeTupleStruct for DynamicSerializer { fn serialize_field( &mut self, - value: &T, + _value: &T, ) -> Result<(), Box> { #[cfg(not(feature = "no_index"))] { - let value = value.serialize(&mut *self)?; - let arr = self.value.downcast_mut::().unwrap(); - arr.push(value); + let _value = _value.serialize(&mut *self)?; + let arr = self._value.downcast_mut::().unwrap(); + arr.push(_value); Ok(()) } #[cfg(feature = "no_index")] @@ -461,7 +470,7 @@ impl SerializeTupleStruct for DynamicSerializer { fn end(self) -> Result> { #[cfg(not(feature = "no_index"))] - return Ok(self.value); + return Ok(self._value); #[cfg(feature = "no_index")] unreachable!() } @@ -471,10 +480,10 @@ impl SerializeMap for DynamicSerializer { type Ok = Dynamic; type Error = Box; - fn serialize_key(&mut self, key: &T) -> Result<(), Box> { + fn serialize_key(&mut self, _key: &T) -> Result<(), Box> { #[cfg(not(feature = "no_object"))] { - self.key = key.serialize(&mut *self)?; + self._key = _key.serialize(&mut *self)?; Ok(()) } #[cfg(feature = "no_object")] @@ -483,11 +492,11 @@ impl SerializeMap for DynamicSerializer { fn serialize_value( &mut self, - value: &T, + _value: &T, ) -> Result<(), Box> { #[cfg(not(feature = "no_object"))] { - let key = mem::take(&mut self.key) + let key = mem::take(&mut self._key) .take_immutable_string() .map_err(|typ| { Box::new(EvalAltResult::ErrorMismatchOutputType( @@ -496,9 +505,9 @@ impl SerializeMap for DynamicSerializer { Position::none(), )) })?; - let value = value.serialize(&mut *self)?; - let map = self.value.downcast_mut::().unwrap(); - map.insert(key, value); + let _value = _value.serialize(&mut *self)?; + let map = self._value.downcast_mut::().unwrap(); + map.insert(key, _value); Ok(()) } #[cfg(feature = "no_object")] @@ -507,22 +516,22 @@ impl SerializeMap for DynamicSerializer { fn serialize_entry( &mut self, - key: &K, - value: &T, + _key: &K, + _value: &T, ) -> Result<(), Box> { #[cfg(not(feature = "no_object"))] { - let key: Dynamic = key.serialize(&mut *self)?; - let key = key.take_immutable_string().map_err(|typ| { + let _key: Dynamic = _key.serialize(&mut *self)?; + let _key = _key.take_immutable_string().map_err(|typ| { Box::new(EvalAltResult::ErrorMismatchOutputType( "string".into(), typ.into(), Position::none(), )) })?; - let value = value.serialize(&mut *self)?; - let map = self.value.downcast_mut::().unwrap(); - map.insert(key, value); + let _value = _value.serialize(&mut *self)?; + let map = self._value.downcast_mut::().unwrap(); + map.insert(_key, _value); Ok(()) } #[cfg(feature = "no_object")] @@ -531,7 +540,7 @@ impl SerializeMap for DynamicSerializer { fn end(self) -> Result> { #[cfg(not(feature = "no_object"))] - return Ok(self.value); + return Ok(self._value); #[cfg(feature = "no_object")] unreachable!() } @@ -543,14 +552,14 @@ impl SerializeStruct for DynamicSerializer { fn serialize_field( &mut self, - key: &'static str, - value: &T, + _key: &'static str, + _value: &T, ) -> Result<(), Box> { #[cfg(not(feature = "no_object"))] { - let value = value.serialize(&mut *self)?; - let map = self.value.downcast_mut::().unwrap(); - map.insert(key.into(), value); + let _value = _value.serialize(&mut *self)?; + let map = self._value.downcast_mut::().unwrap(); + map.insert(_key.into(), _value); Ok(()) } #[cfg(feature = "no_object")] @@ -559,7 +568,7 @@ impl SerializeStruct for DynamicSerializer { fn end(self) -> Result> { #[cfg(not(feature = "no_object"))] - return Ok(self.value); + return Ok(self._value); #[cfg(feature = "no_object")] unreachable!() } diff --git a/src/serde/str.rs b/src/serde/str.rs index 23e2c3d2..7fb1c9cc 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -6,7 +6,7 @@ use crate::utils::ImmutableString; use serde::de::{Deserializer, Visitor}; -use crate::stdlib::any::type_name; +use crate::stdlib::{any::type_name, boxed::Box}; /// Deserializer for `ImmutableString`. pub struct ImmutableStringDeserializer<'a> { diff --git a/src/settings.rs b/src/settings.rs index 60c1a22a..d638499c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,12 +1,17 @@ //! Configuration settings for `Engine`. use crate::engine::Engine; -use crate::module::ModuleResolver; use crate::optimize::OptimizationLevel; use crate::packages::PackageLibrary; use crate::token::{is_valid_identifier, Token}; -use crate::stdlib::{boxed::Box, format, string::String}; +#[cfg(not(feature = "no_module"))] +use crate::module::ModuleResolver; + +use crate::stdlib::{format, string::String}; + +#[cfg(not(feature = "no_module"))] +use crate::stdlib::boxed::Box; impl Engine { /// Load a new package into the `Engine`. @@ -41,21 +46,21 @@ impl Engine { /// infinite recursion and stack overflows. #[cfg(not(feature = "unchecked"))] pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self { - self.max_call_stack_depth = levels; + self.limits.max_call_stack_depth = levels; self } /// The maximum levels of function calls allowed for a script. #[cfg(not(feature = "unchecked"))] pub fn max_call_levels(&self) -> usize { - self.max_call_stack_depth + self.limits.max_call_stack_depth } /// Set the maximum number of operations allowed for a script to run to avoid /// consuming too much resources (0 for unlimited). #[cfg(not(feature = "unchecked"))] pub fn set_max_operations(&mut self, operations: u64) -> &mut Self { - self.max_operations = if operations == u64::MAX { + self.limits.max_operations = if operations == u64::MAX { 0 } else { operations @@ -66,20 +71,20 @@ impl Engine { /// The maximum number of operations allowed for a script to run (0 for unlimited). #[cfg(not(feature = "unchecked"))] pub fn max_operations(&self) -> u64 { - self.max_operations + self.limits.max_operations } /// Set the maximum number of imported modules allowed for a script. #[cfg(not(feature = "unchecked"))] pub fn set_max_modules(&mut self, modules: usize) -> &mut Self { - self.max_modules = modules; + self.limits.max_modules = modules; self } /// The maximum number of imported modules allowed for a script. #[cfg(not(feature = "unchecked"))] pub fn max_modules(&self) -> usize { - self.max_modules + self.limits.max_modules } /// Set the depth limits for expressions (0 for unlimited). @@ -89,12 +94,12 @@ impl Engine { max_expr_depth: usize, max_function_expr_depth: usize, ) -> &mut Self { - self.max_expr_depth = if max_expr_depth == usize::MAX { + self.limits.max_expr_depth = if max_expr_depth == usize::MAX { 0 } else { max_expr_depth }; - self.max_function_expr_depth = if max_function_expr_depth == usize::MAX { + self.limits.max_function_expr_depth = if max_function_expr_depth == usize::MAX { 0 } else { max_function_expr_depth @@ -105,33 +110,33 @@ impl Engine { /// The depth limit for expressions (0 for unlimited). #[cfg(not(feature = "unchecked"))] pub fn max_expr_depth(&self) -> usize { - self.max_expr_depth + self.limits.max_expr_depth } /// The depth limit for expressions in functions (0 for unlimited). #[cfg(not(feature = "unchecked"))] pub fn max_function_expr_depth(&self) -> usize { - self.max_function_expr_depth + self.limits.max_function_expr_depth } /// Set the maximum length of strings (0 for unlimited). #[cfg(not(feature = "unchecked"))] pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self { - self.max_string_size = if max_size == usize::MAX { 0 } else { max_size }; + self.limits.max_string_size = if max_size == usize::MAX { 0 } else { max_size }; self } /// The maximum length of strings (0 for unlimited). #[cfg(not(feature = "unchecked"))] pub fn max_string_size(&self) -> usize { - self.max_string_size + self.limits.max_string_size } /// Set the maximum length of arrays (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self { - self.max_array_size = if max_size == usize::MAX { 0 } else { max_size }; + self.limits.max_array_size = if max_size == usize::MAX { 0 } else { max_size }; self } @@ -139,14 +144,14 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] pub fn max_array_size(&self) -> usize { - self.max_array_size + self.limits.max_array_size } /// Set the maximum length of object maps (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self { - self.max_map_size = if max_size == usize::MAX { 0 } else { max_size }; + self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size }; self } @@ -154,7 +159,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] pub fn max_map_size(&self) -> usize { - self.max_map_size + self.limits.max_map_size } /// Set the module resolution service used by the `Engine`. diff --git a/src/token.rs b/src/token.rs index 72e733d3..775e78db 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1549,7 +1549,10 @@ pub fn lex<'a, 'e>(input: &'a [&'a str], engine: &'e Engine) -> TokenIterator<'a TokenIterator { engine, state: TokenizeState { - max_string_size: engine.max_string_size, + #[cfg(not(feature = "unchecked"))] + max_string_size: engine.limits.max_string_size, + #[cfg(feature = "unchecked")] + max_string_size: 0, non_unary: false, comment_level: 0, end_with_none: false, diff --git a/tests/call_fn.rs b/tests/call_fn.rs index e96ee63d..9808ff70 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -166,9 +166,9 @@ fn test_fn_ptr_curry_call() -> Result<(), Box> { module.set_raw_fn( "call_with_arg", &[TypeId::of::(), TypeId::of::()], - |engine: &Engine, module: &Module, args: &mut [&mut Dynamic]| { + |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { let fn_ptr = std::mem::take(args[0]).cast::(); - fn_ptr.call_dynamic(engine, module, None, [std::mem::take(args[1])]) + fn_ptr.call_dynamic(engine, lib, None, [std::mem::take(args[1])]) }, ); diff --git a/tests/modules.rs b/tests/modules.rs index b7393f14..fd049815 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -245,7 +245,7 @@ fn test_module_from_ast() -> Result<(), Box> { *engine .consume(r#"import "testing" as ttt; ttt::hidden()"#) .expect_err("should error"), - EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "ttt::hidden" + EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "ttt::hidden ()" )); Ok(()) From 5dc244ddc8cd37e0ebbef0d54f8161d729cc3973 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 26 Jul 2020 16:23:47 +0800 Subject: [PATCH 6/6] 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'