Merge pull request #202 from schungx/master

Eliminate compilation warnings.
This commit is contained in:
Stephen Chung 2020-07-26 16:24:32 +08:00 committed by GitHub
commit 5450f66278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 884 additions and 471 deletions

View File

@ -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

View File

@ -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)
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)
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)
@ -64,28 +65,29 @@ 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)
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 +98,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 +114,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)

View File

@ -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/).

16
doc/src/about/license.md Normal file
View File

@ -0,0 +1,16 @@
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 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,
without any additional terms or conditions.

View File

@ -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.

View File

@ -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.

View File

@ -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"

View File

@ -22,19 +22,31 @@ fn main() -> Result<(), Box<EvalAltResult>>
}
```
`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::<i64>("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::<i64>("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::<String>("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::<i64>("hello_world.rhai".into())?; // 'eval_file' takes a 'PathBuf'
```

View File

@ -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` |
| `&`, <code>\|</code>, | `&=`, <code>\|=</code> | `INT`, `bool` |
| `&&`, <code>\|\|</code> | | `bool` |
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |

View File

@ -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.

View File

@ -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.

View File

@ -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"
```

View File

@ -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).

View File

@ -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;
```

View File

@ -66,8 +66,8 @@ let mut engine = Engine::new();
engine.register_type::<TestStruct>();
```
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.

View File

@ -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<Dynamic, Box<EvalAltResult>> {
Ok((42_i64).into()) // standard types can use 'into()'
Ok((42_i64).into()) // standard types can use 'into()'
}
let mut engine = Engine::new();

View File

@ -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<Module, Box<EvalAltResult>> {
// Check module path.
if is_valid_module_path(path) {

View File

@ -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

View File

@ -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<String>` or `Arc<String>` 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.

View File

@ -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).<br/>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).<br/>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:

View File

@ -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

View File

@ -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.

View File

@ -6,7 +6,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]

View File

@ -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;

View File

@ -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::<T>().to_string(), name.to_string());
.insert(type_name::<T>().into(), name.into());
self
}

View File

@ -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<T: Variant + Clone>(&self) -> bool {
match self {
Target::Ref(r) => r.is::<T>(),
Target::Value(r) => r.is::<T>(),
#[cfg(not(feature = "no_index"))]
Target::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(),
}
}
/// 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::<String>().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<T: Into<Dynamic>> From<T> 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<Box<dyn ModuleResolver>>,
/// 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<Dynamic>,
chain_type: ChainType,
level: usize,
mut new_val: Option<Dynamic>,
mut _new_val: Option<Dynamic>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
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];
@ -639,9 +700,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),
@ -655,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];
@ -686,13 +746,13 @@ impl Engine {
// xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(),
// {xxx:map}.id = ???
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
Expr::Property(x) if target.is::<Map>() && _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))
}
@ -706,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,
)
@ -750,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))
}
@ -777,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))?;
@ -812,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))
}
@ -835,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,
@ -911,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,
@ -981,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<Target<'a>, Box<EvalAltResult>> {
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))?;
@ -1022,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::<ImmutableString>()
.ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
@ -1043,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))?;
@ -1064,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 {
@ -1077,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(),
@ -1254,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 {
@ -1277,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())
}
@ -1285,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())
}
@ -1641,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::<ImmutableString>()
{
#[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();
@ -1668,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
@ -1698,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<Dynamic, Box<EvalAltResult>>,
) -> Result<Dynamic, Box<EvalAltResult>> {
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<Dynamic, Box<EvalAltResult>>,
@ -1708,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;
}
@ -1768,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(),
)))
@ -1814,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(),
)));

View File

@ -22,11 +22,10 @@ macro_rules! impl_args {
fn into_vec(self) -> StaticVec<Dynamic> {
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
}
}

View File

@ -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<bool>,
level: usize,
_level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
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::<StaticVec<Dynamic>>();
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::<FnPtr>() {
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
// FnPtr call
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
@ -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::<FnPtr>() {
} else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
// FnPtr call on object
let fn_ptr = idx.remove(0).cast::<FnPtr>();
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
@ -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::<FnPtr>() {
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
// Curry call
let fn_ptr = obj.downcast_ref::<FnPtr>().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::<Map>() {
if let Some(val) = map.get(fn_name) {
if let Some(val) = map.get(_fn_name) {
if let Some(f) = val.downcast_ref::<FnPtr>() {
// 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))?;
@ -864,10 +879,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::<ImmutableString>() {
"&str | ImmutableString | String"
} else {
self.map_type_name((*a).type_name())
})
.collect::<Vec<_>>()
.join(", ")
),
pos,
)))
}
_ => Err(err),

View File

@ -2,16 +2,23 @@
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, rc::Rc, string::String, sync::Arc, 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;
#[cfg(feature = "sync")]
use crate::stdlib::sync::Arc;
/// Trait that maps to `Send + Sync` only under the `sync` feature.
#[cfg(feature = "sync")]
@ -82,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,

View File

@ -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

View File

@ -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")]

View File

@ -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<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
&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<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
getter: impl Fn(&mut A, B) -> FuncReturn<T> + 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<Item = &(String, FnAccess, StaticVec<TypeId>, 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();

View File

@ -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<Stmt>,
functions: Vec<ScriptFnDef>,
_functions: Vec<ScriptFnDef>,
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);
});
}

View File

@ -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,19 +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, Shl, Shr, 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<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> {
x.checked_add(&y).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic(
@ -33,6 +40,7 @@ pub(crate) fn add<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> {
})
}
// Checked subtract
#[cfg(not(feature = "unchecked"))]
pub(crate) fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> {
x.checked_sub(&y).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic(
@ -42,6 +50,7 @@ pub(crate) fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> {
})
}
// Checked multiply
#[cfg(not(feature = "unchecked"))]
pub(crate) fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> {
x.checked_mul(&y).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic(
@ -51,6 +60,7 @@ pub(crate) fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> {
})
}
// Checked divide
#[cfg(not(feature = "unchecked"))]
pub(crate) fn div<T>(x: T, y: T) -> FuncReturn<T>
where
T: Display + CheckedDiv + PartialEq + Zero,
@ -71,6 +81,7 @@ where
})
}
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
#[cfg(not(feature = "unchecked"))]
pub(crate) fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> {
x.checked_neg().ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic(
@ -80,6 +91,7 @@ pub(crate) fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> {
})
}
// Checked absolute
#[cfg(not(feature = "unchecked"))]
pub(crate) fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> FuncReturn<T> {
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
// when the number is ::MIN instead of returning ::MIN itself.
@ -95,26 +107,32 @@ pub(crate) fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> FuncRetu
}
}
// Unchecked add - may panic on overflow
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn add_u<T: Add>(x: T, y: T) -> FuncReturn<<T as Add>::Output> {
Ok(x + y)
}
// Unchecked subtract - may panic on underflow
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn sub_u<T: Sub>(x: T, y: T) -> FuncReturn<<T as Sub>::Output> {
Ok(x - y)
}
// Unchecked multiply - may panic on overflow
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn mul_u<T: Mul>(x: T, y: T) -> FuncReturn<<T as Mul>::Output> {
Ok(x * y)
}
// Unchecked divide - may panic when dividing by zero
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn div_u<T: Div>(x: T, y: T) -> FuncReturn<<T as Div>::Output> {
Ok(x / y)
}
// Unchecked negative - may panic on overflow
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn neg_u<T: Neg>(x: T) -> FuncReturn<<T as Neg>::Output> {
Ok(-x)
}
// Unchecked absolute - may panic on overflow
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn abs_u<T>(x: T) -> FuncReturn<<T as Neg>::Output>
where
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
@ -137,6 +155,7 @@ fn binary_xor<T: BitXor>(x: T, y: T) -> FuncReturn<<T as BitXor>::Output> {
Ok(x ^ y)
}
// Checked left-shift
#[cfg(not(feature = "unchecked"))]
pub(crate) fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> {
// Cannot shift by a negative number of bits
if y < 0 {
@ -154,6 +173,7 @@ pub(crate) fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> {
})
}
// Checked right-shift
#[cfg(not(feature = "unchecked"))]
pub(crate) fn shr<T: Display + CheckedShr>(x: T, y: INT) -> FuncReturn<T> {
// Cannot shift by a negative number of bits
if y < 0 {
@ -171,14 +191,17 @@ pub(crate) fn shr<T: Display + CheckedShr>(x: T, y: INT) -> FuncReturn<T> {
})
}
// Unchecked left-shift - may panic if shifting by a negative number of bits
#[cfg(feature = "unchecked")]
pub(crate) fn shl_u<T: Shl<T>>(x: T, y: T) -> FuncReturn<<T as Shl<T>>::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<T: Shr<T>>(x: T, y: T) -> FuncReturn<<T as Shr<T>>::Output> {
Ok(x.shr(y))
}
// Checked modulo
#[cfg(not(feature = "unchecked"))]
pub(crate) fn modulo<T: Display + CheckedRem>(x: T, y: T) -> FuncReturn<T> {
x.checked_rem(&y).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic(
@ -188,10 +211,12 @@ pub(crate) fn modulo<T: Display + CheckedRem>(x: T, y: T) -> FuncReturn<T> {
})
}
// Unchecked modulo - may panic if dividing by zero
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn modulo_u<T: Rem>(x: T, y: T) -> FuncReturn<<T as Rem>::Output> {
Ok(x % y)
}
// Checked power
#[cfg(not(feature = "unchecked"))]
pub(crate) fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> {
#[cfg(not(feature = "only_i32"))]
if y > (u32::MAX as INT) {
@ -229,6 +254,7 @@ pub(crate) fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> {
}
}
// 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<INT> {
Ok(x.pow(y as u32))
}
@ -239,6 +265,7 @@ pub(crate) fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn<FLOAT> {
}
// Checked power
#[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "unchecked"))]
pub(crate) fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn<FLOAT> {
// Raise to power that is larger than an i32
if y > (i32::MAX as INT) {

View File

@ -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<T: Variant + Clone>(list: &mut Array, item: T) -> FuncReturn<()> {
@ -26,7 +30,7 @@ fn ins<T: Variant + Clone>(list: &mut Array, position: INT, item: T) -> FuncRetu
Ok(())
}
fn pad<T: Variant + Clone>(
engine: &Engine,
_engine: &Engine,
_: &Module,
args: &mut [&mut Dynamic],
) -> FuncReturn<()> {
@ -34,10 +38,13 @@ fn pad<T: Variant + Clone>(
// 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(),
)));

View File

@ -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<Vec<Dynamic>> {
Ok(map.iter().map(|(k, _)| k.clone().into()).collect())
}
#[cfg(not(feature = "no_index"))]
fn map_get_values(map: &mut Map) -> FuncReturn<Vec<Dynamic>> {
Ok(map.iter().map(|(_, v)| v.clone()).collect())
}

View File

@ -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, {

View File

@ -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;
@ -74,6 +73,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))
}

View File

@ -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::<ImmutableString>(), TypeId::of::<INT>(), TypeId::of::<char>()],
|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(),
)));

View File

@ -1,11 +1,21 @@
#![cfg(not(feature = "no_std"))]
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;
use crate::module::FuncReturn;
use crate::parser::INT;
use crate::result::EvalAltResult;
#[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT;
#[cfg(feature = "no_float")]
use crate::parser::INT;
#[cfg(feature = "no_float")]
#[cfg(not(feature = "unchecked"))]
use crate::token::Position;
#[cfg(not(target_arch = "wasm32"))]
@ -14,9 +24,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()));

View File

@ -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<String>,
/// 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<Expr>)>),
/// 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<StaticVec<((String, Position), Option<(String, Position)>)>>),
}
@ -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<Expr, ParseError> {
#[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<Expr, ParseError> {
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<Expr, ParseError> {
#[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<Expr, ParseError> {
#[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<Expr, ParseError> {
#[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<Expr, ParseError> {
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<Expr, ParseError> {
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<Expr, ParseError> {
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<Expr, ParseError> {
Ok(match (lhs, rhs) {
// idx_lhs[idx_expr].rhs
@ -2048,6 +2093,8 @@ fn parse_binary_op(
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
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<Expr, ParseError> {
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<Stmt, ParseError> {
// 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<Stmt, ParseError> {
// 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<Stmt, ParseError> {
// 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<Stmt, ParseError> {
// 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<Stmt, ParseError> {
// 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<Stmt, ParseError> {
// 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<Stmt, ParseError> {
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<Stmt, ParseError> {
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<ScriptFnDef, ParseError> {
#[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<AST, ParseError> {
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<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements: Vec<Stmt> = 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 {

View File

@ -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<EvalAltResult>, Position),
/// Access to `this` that is not bounded.
ErrorUnboundedThis(Position),

View File

@ -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<Item = Entry<'a>> {
self.0.into_iter()
}

View File

@ -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<T>(&self) -> Result<T, Box<EvalAltResult>> {
self.type_error_str(type_name::<T>())
}
/// Shortcut for a type conversion error.
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
type_name::<T>().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<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
#[cfg(not(feature = "no_float"))]
return self
.value
.downcast_ref::<f32>()
.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<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
#[cfg(not(feature = "no_float"))]
return self
.value
.downcast_ref::<f64>()
.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<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))]
return self.value.downcast_ref::<Array>().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<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<Map>().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<Item = &'a Dynamic>,
@ -502,6 +510,7 @@ where
values: VALUES,
}
#[cfg(not(feature = "no_object"))]
impl<'a, KEYS, VALUES> IterateMap<'a, KEYS, VALUES>
where
KEYS: Iterator<Item = &'a ImmutableString>,

View File

@ -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<Self::Ok, Box<EvalAltResult>> {
#[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<Self::SerializeTupleVariant, Box<EvalAltResult>> {
#[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<Self::SerializeStructVariant, Box<EvalAltResult>> {
#[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<T: ?Sized + Serialize>(
&mut self,
value: &T,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))]
{
let value = value.serialize(&mut *self)?;
let arr = self.value.downcast_mut::<Array>().unwrap();
arr.push(value);
let _value = _value.serialize(&mut *self)?;
let arr = self._value.downcast_mut::<Array>().unwrap();
arr.push(_value);
Ok(())
}
#[cfg(feature = "no_index")]
@ -407,7 +416,7 @@ impl SerializeSeq for DynamicSerializer {
// Close the sequence.
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[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<T: ?Sized + Serialize>(
&mut self,
value: &T,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))]
{
let value = value.serialize(&mut *self)?;
let arr = self.value.downcast_mut::<Array>().unwrap();
arr.push(value);
let _value = _value.serialize(&mut *self)?;
let arr = self._value.downcast_mut::<Array>().unwrap();
arr.push(_value);
Ok(())
}
#[cfg(feature = "no_index")]
@ -434,7 +443,7 @@ impl SerializeTuple for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[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<T: ?Sized + Serialize>(
&mut self,
value: &T,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))]
{
let value = value.serialize(&mut *self)?;
let arr = self.value.downcast_mut::<Array>().unwrap();
arr.push(value);
let _value = _value.serialize(&mut *self)?;
let arr = self._value.downcast_mut::<Array>().unwrap();
arr.push(_value);
Ok(())
}
#[cfg(feature = "no_index")]
@ -461,7 +470,7 @@ impl SerializeTupleStruct for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[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<EvalAltResult>;
fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<(), Box<EvalAltResult>> {
fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> Result<(), Box<EvalAltResult>> {
#[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<T: ?Sized + Serialize>(
&mut self,
value: &T,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[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::<Map>().unwrap();
map.insert(key, value);
let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<Map>().unwrap();
map.insert(key, _value);
Ok(())
}
#[cfg(feature = "no_object")]
@ -507,22 +516,22 @@ impl SerializeMap for DynamicSerializer {
fn serialize_entry<K: ?Sized + Serialize, T: ?Sized + Serialize>(
&mut self,
key: &K,
value: &T,
_key: &K,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[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::<Map>().unwrap();
map.insert(key, value);
let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<Map>().unwrap();
map.insert(_key, _value);
Ok(())
}
#[cfg(feature = "no_object")]
@ -531,7 +540,7 @@ impl SerializeMap for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[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<T: ?Sized + Serialize>(
&mut self,
key: &'static str,
value: &T,
_key: &'static str,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))]
{
let value = value.serialize(&mut *self)?;
let map = self.value.downcast_mut::<Map>().unwrap();
map.insert(key.into(), value);
let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<Map>().unwrap();
map.insert(_key.into(), _value);
Ok(())
}
#[cfg(feature = "no_object")]
@ -559,7 +568,7 @@ impl SerializeStruct for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))]
return Ok(self.value);
return Ok(self._value);
#[cfg(feature = "no_object")]
unreachable!()
}

View File

@ -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> {

View File

@ -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`.

View File

@ -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(

View File

@ -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,

View File

@ -166,9 +166,9 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
module.set_raw_fn(
"call_with_arg",
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|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::<FnPtr>();
fn_ptr.call_dynamic(engine, module, None, [std::mem::take(args[1])])
fn_ptr.call_dynamic(engine, lib, None, [std::mem::take(args[1])])
},
);

View File

@ -245,7 +245,7 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
*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(())