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 * [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) * [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 Unless explicitly stated otherwise, any contribution intentionally submitted
for inclusion in this crate, as defined in the Apache-2.0 license, shall 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) 1. [Features](about/features.md)
2. [Supported Targets and Builds](about/targets.md) 2. [Supported Targets and Builds](about/targets.md)
3. [What Rhai Isn't](about/non-design.md) 3. [What Rhai Isn't](about/non-design.md)
4. [Related Resources](about/related.md) 4. [Licensing](about/license.md)
3. [Getting Started](start/index.md) 5. [Related Resources](about/related.md)
2. [Getting Started](start/index.md)
1. [Online Playground](start/playground.md) 1. [Online Playground](start/playground.md)
2. [Install the Rhai Crate](start/install.md) 2. [Install the Rhai Crate](start/install.md)
3. [Optional Features](start/features.md) 3. [Optional Features](start/features.md)
@ -18,14 +19,14 @@ The Rhai Scripting Language
5. [Examples](start/examples/index.md) 5. [Examples](start/examples/index.md)
1. [Rust](start/examples/rust.md) 1. [Rust](start/examples/rust.md)
2. [Scripts](start/examples/scripts.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) 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) 3. [Call a Rhai Function from Rust](engine/call-fn.md)
4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md) 4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md)
5. [Evaluate Expressions Only](engine/expressions.md) 5. [Evaluate Expressions Only](engine/expressions.md)
6. [Raw Engine](engine/raw.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) 1. [Traits](rust/traits.md)
2. [Register a Rust Function](rust/functions.md) 2. [Register a Rust Function](rust/functions.md)
1. [String Parameters in Rust Functions](rust/strings.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) 4. [Printing Custom Types](rust/print-custom.md)
9. [Scope - Initializing and Maintaining State](rust/scope.md) 9. [Scope - Initializing and Maintaining State](rust/scope.md)
10. [Engine Configuration Options](rust/options.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) 1. [Comments](language/comments.md)
2. [Values and Types](language/values-and-types.md) 2. [Values and Types](language/values-and-types.md)
1. [Dynamic Values](language/dynamic.md) 1. [Dynamic Values](language/dynamic.md)
@ -64,28 +65,29 @@ The Rhai Scripting Language
5. [Variables](language/variables.md) 5. [Variables](language/variables.md)
6. [Constants](language/constants.md) 6. [Constants](language/constants.md)
7. [Logic Operators](language/logic.md) 7. [Logic Operators](language/logic.md)
8. [If Statement](language/if.md) 8. [Other Operators](language/other-op.md)
9. [While Loop](language/while.md) 9. [If Statement](language/if.md)
10. [Loop Statement](language/loop.md) 10. [While Loop](language/while.md)
11. [For Loop](language/for.md) 11. [Loop Statement](language/loop.md)
12. [Return Values](language/return.md) 12. [For Loop](language/for.md)
13. [Throw Exception on Error](language/throw.md) 13. [Return Values](language/return.md)
14. [Functions](language/functions.md) 14. [Throw Exception on Error](language/throw.md)
15. [Functions](language/functions.md)
1. [Call Method as Function](language/method.md) 1. [Call Method as Function](language/method.md)
2. [Overloading](language/overload.md) 2. [Overloading](language/overload.md)
3. [Namespaces](language/fn-namespaces.md) 3. [Namespaces](language/fn-namespaces.md)
4. [Function Pointers](language/fn-ptr.md) 4. [Function Pointers](language/fn-ptr.md)
5. [Anonymous Functions](language/fn-anon.md) 5. [Anonymous Functions](language/fn-anon.md)
6. [Currying](language/fn-curry.md) 6. [Currying](language/fn-curry.md)
15. [Print and Debug](language/print-debug.md) 16. [Print and Debug](language/print-debug.md)
16. [Modules](language/modules/index.md) 17. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md) 1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
2. [Import Modules](language/modules/import.md) 2. [Import Modules](language/modules/import.md)
3. [Create from Rust](rust/modules/index.md) 3. [Create from Rust](rust/modules/index.md)
4. [Create from AST](language/modules/ast.md) 4. [Create from AST](language/modules/ast.md)
5. [Module Resolvers](rust/modules/resolvers.md) 5. [Module Resolvers](rust/modules/resolvers.md)
1. [Custom Implementation](rust/modules/imp-resolver.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) 1. [Checked Arithmetic](safety/checked.md)
2. [Sand-Boxing](safety/sandbox.md) 2. [Sand-Boxing](safety/sandbox.md)
3. [Maximum Length of Strings](safety/max-string-size.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) 7. [Maximum Number of Modules](safety/max-modules.md)
8. [Maximum Call Stack Depth](safety/max-call-stack.md) 8. [Maximum Call Stack Depth](safety/max-call-stack.md)
9. [Maximum Statement Depth](safety/max-stmt-depth.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) 1. [Object-Oriented Programming (OOP)](language/oop.md)
2. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md) 2. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
3. [Script Optimization](engine/optimize/index.md) 3. [Script Optimization](engine/optimize/index.md)
@ -112,7 +114,7 @@ The Rhai Scripting Language
2. [Custom Operators](engine/custom-op.md) 2. [Custom Operators](engine/custom-op.md)
3. [Extending with Custom Syntax](engine/custom-syntax.md) 3. [Extending with Custom Syntax](engine/custom-syntax.md)
6. [Eval Statement](language/eval.md) 6. [Eval Statement](language/eval.md)
9. [Appendix](appendix/index.md) 8. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md) 1. [Keywords](appendix/keywords.md)
2. [Operators and Symbols](appendix/operators.md) 2. [Operators and Symbols](appendix/operators.md)
3. [Literals](appendix/literals.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. 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/). 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. * 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 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). * 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 * 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. 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 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. 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 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. 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. 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: This section covers advanced features such as:
* Simulated [Object Oriented Programming][OOP]. * Simulated [Object Oriented Programming (OOP)][OOP].
* [`serde`] integration. * [`serde`] integration.
@ -13,4 +13,6 @@ This section covers advanced features such as:
* [Domain-Specific Languages][DSL]. * [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. * The dreaded (or beloved for those with twisted tastes) [`eval`] statement.

View File

@ -1,5 +1,7 @@
{ {
"version": "0.18.0", "version": "0.18.0",
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
"rootUrl": "", "rootUrl": "",
"rootUrlX": "/rhai", "rootUrlX": "/rhai",
"rootUrlXX": "/rhai/vnext" "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::EvalAltResult` is the standard Rhai error type, which is a Rust `enum` containing all errors encountered
Rhai is very strict here. 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. There are two ways to specify the return type - _turbofish_ notation, or type inference.
Use [`Dynamic`] for uncertain return types.
```rust ```rust
let result = engine.eval::<i64>("40 + 2")?; // return type is i64, specified using 'turbofish' notation 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 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. application code storage space.
Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of 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 Built-in Operators
------------------ ------------------
@ -20,7 +23,7 @@ Built-in Operators
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` | | `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` |
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) | | `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
| `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` | | `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` |
| `&`, `|`, | `&=`, `|=` | `INT`, `bool` | | `&`, <code>\|</code>, | `&=`, <code>\|=</code> | `INT`, `bool` |
| `&&`, `||` | | `bool` | | `&&`, <code>\|\|</code> | | `bool` |
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` | | `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` | | `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |

View File

@ -3,7 +3,8 @@
{{#include ../links.md}} {{#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; 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. `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 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 '`{`' .. '`}`', all branches of the `if` statement must be enclosed within braces '`{`' .. '`}`',
even when there is only one statement inside the branch. 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 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}} {{#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 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 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, 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. 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` 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 `ImmutableString` is cheap to clone, but expensive to take a mutable reference (because the underlying
string must be cloned to make a private copy). 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>(); 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 To use native custom types, methods and functions in Rhai scripts, simply register them
using one of the `Engine::register_XXX` API. using one of the `Engine::register_XXX` API.

View File

@ -11,8 +11,8 @@ see [fallible functions]({{rootUrl}}/rust/fallible.md)).
```rust ```rust
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString}; use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString};
use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn' use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn'
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn' use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
// Normal function that returns a standard type // Normal function that returns a standard type
// Remember to use 'ImmutableString' and not 'String' // 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' // Function that returns a 'Dynamic' value - must return a 'Result'
fn get_any_value() -> Result<Dynamic, Box<EvalAltResult>> { 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(); let mut engine = Engine::new();

View File

@ -30,7 +30,7 @@ impl ModuleResolver for MyModuleResolver {
&self, &self,
engine: &Engine, // reference to the current 'Engine' engine: &Engine, // reference to the current 'Engine'
path: &str, // the module path path: &str, // the module path
pos: Position, // location of the 'import' statement pos: Position, // position of the 'import' statement
) -> Result<Module, Box<EvalAltResult>> { ) -> Result<Module, Box<EvalAltResult>> {
// Check module path. // Check module path.
if is_valid_module_path(path) { if is_valid_module_path(path) {

View File

@ -12,7 +12,9 @@ fn to_int(num) {
print("Ha! Gotcha! " + 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 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 Internally, Rhai uses _immutable_ [strings] instead of the Rust `String` type. This is mainly to avoid excessive
cloning when passing function arguments. cloning when passing function arguments.
The encapsulated immutable string type is `ImmutableString`. It is cheap to clone (just an `Rc` or `Arc` reference Rhai's internal string type is `ImmutableString` (basically `Rc<String>` or `Arc<String>` depending on the [`sync`] feature).
count increment 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`) Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`)
for the best performance with Rhai. 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: A number of examples can be found in the `examples` directory:
| Example | Description | | 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. | | [`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`](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. | | [`custom_types_and_methods`]({{repoTree}}/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. | | [`hello`]({{repoTree}}/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`]. | | [`reuse_scope`]({{repoTree}}/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. | | [`rhai_runner`]({{repoTree}}/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. | | [`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`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | Shows how to register a simple function. | | [`simple_fn`]({{repoTree}}/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. | | [`strings`]({{repoTree}}/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. | | [`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 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). 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: To illustrate `no-std` builds, a number of sample applications are available under the `no_std` directory:
| Sample | Description | Optimization | Allocator | Panics | | 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 | | [`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: `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: There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` directory:
| Script | Description | | Script | Description |
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] | | [`array.rhai`]({{repoTree}}/scripts/array.rhai) | [Arrays] |
| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations | | [`assignment.rhai`]({{repoTree}}/scripts/assignment.rhai) | Variable declarations |
| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments | | [`comments.rhai`]({{repoTree}}/scripts/comments.rhai) | Just comments |
| [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`]({{rootUrl}}/language/for.md) loops | | [`for1.rhai`]({{repoTree}}/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] | | [`for2.rhai`]({{repoTree}}/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_decl1.rhai`]({{repoTree}}/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_decl2.rhai`]({{repoTree}}/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 | | [`function_decl3.rhai`]({{repoTree}}/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 | | [`if1.rhai`]({{repoTree}}/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 | | [`loop.rhai`]({{repoTree}}/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] | | [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] |
| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition | | [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | Just simple addition |
| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication | | [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | Simple addition and multiplication |
| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis | | [`op3.rhai`]({{repoTree}}/scripts/op3.rhai) | Change evaluation order with parenthesis |
| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations | | [`string.rhai`]({{repoTree}}/scripts/string.rhai) | [String] operations |
| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations | | [`strings_map.rhai`]({{repoTree}}/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 | | [`while.rhai`]({{repoTree}}/scripts/while.rhai) | [`while`]({{rootUrl}}/language/while.md) loop |
Benchmark Scripts Benchmark Scripts
@ -34,12 +34,12 @@ Benchmark Scripts
The following scripts are for benchmarking the speed of Rhai: The following scripts are for benchmarking the speed of Rhai:
| Scripts | Description | | 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). | | [`speed_test.rhai`]({{repoTree}}/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. | | [`primes.rhai`]({{repoTree}}/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. | | [`fibonacci.rhai`]({{repoTree}}/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. | | [`mat_mul.rhai`]({{repoTree}}/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. |
Running Example Scripts 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_module` | Disable loading external [modules]. |
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | | `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. | | `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 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`), 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]. nor loading external [modules].
This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware. 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" edition = "2018"
authors = ["Stephen Chung"] authors = ["Stephen Chung"]
description = "no-std test application" 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" repository = "https://github.com/jonathandturner/rhai"
[dependencies] [dependencies]

View File

@ -16,12 +16,16 @@ use crate::engine::Map;
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, Any, TypeId}, any::{type_name, Any, TypeId},
boxed::Box, boxed::Box,
collections::HashMap,
fmt, fmt,
string::String, 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(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::stdlib::time::Instant; use crate::stdlib::time::Instant;

View File

@ -1,30 +1,33 @@
//! Module that defines the extern API of `Engine`. //! Module that defines the extern API of `Engine`.
use crate::any::{Dynamic, Variant}; 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::error::ParseError;
use crate::fn_args::FuncArgs;
use crate::fn_native::{IteratorFn, SendSync}; use crate::fn_native::{IteratorFn, SendSync};
use crate::fn_register::RegisterFn;
use crate::module::{FuncReturn, Module}; use crate::module::{FuncReturn, Module};
use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::parser::AST; use crate::parser::AST;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::token::{lex, Position}; 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"))] #[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"))] #[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::{ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
boxed::Box, boxed::Box,
mem, mem,
string::{String, ToString},
}; };
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -293,7 +296,7 @@ impl Engine {
self.type_names self.type_names
.as_mut() .as_mut()
.unwrap() .unwrap()
.insert(type_name::<T>().to_string(), name.to_string()); .insert(type_name::<T>().into(), name.into());
self self
} }

View File

@ -1,13 +1,13 @@
//! Main module defining the script evaluation `Engine`. //! 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::calc_fn_hash;
use crate::fn_call::run_builtin_op_assignment; use crate::fn_call::run_builtin_op_assignment;
use crate::fn_native::{CallableFunction, Callback, FnPtr}; 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::optimize::OptimizationLevel;
use crate::packages::{Package, PackagesCollection, StandardPackage}; 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::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
@ -15,8 +15,23 @@ use crate::syntax::{CustomSyntax, EvalContext};
use crate::token::Position; use crate::token::Position;
use crate::utils::StaticVec; 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::{ use crate::stdlib::{
any::TypeId,
borrow::Cow, borrow::Cow,
boxed::Box, boxed::Box,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@ -26,6 +41,9 @@ use crate::stdlib::{
vec::Vec, vec::Vec,
}; };
#[cfg(not(feature = "no_index"))]
use crate::stdlib::any::TypeId;
/// Variable-sized array of `Dynamic` values. /// Variable-sized array of `Dynamic` values.
/// ///
/// Not available under the `no_index` feature. /// Not available under the `no_index` feature.
@ -66,13 +84,6 @@ pub const MAX_EXPR_DEPTH: usize = 128;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; 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_PRINT: &str = "print";
pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_DEBUG: &str = "debug";
pub const KEYWORD_TYPE_OF: &str = "type_of"; 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_FN_PTR_CURRY: &str = "curry";
pub const KEYWORD_THIS: &str = "this"; pub const KEYWORD_THIS: &str = "this";
pub const FN_TO_STRING: &str = "to_string"; pub const FN_TO_STRING: &str = "to_string";
#[cfg(not(feature = "no_object"))]
pub const FN_GET: &str = "get$"; pub const FN_GET: &str = "get$";
#[cfg(not(feature = "no_object"))]
pub const FN_SET: &str = "set$"; pub const FN_SET: &str = "set$";
#[cfg(not(feature = "no_index"))]
pub const FN_IDX_GET: &str = "index$get$"; pub const FN_IDX_GET: &str = "index$get$";
#[cfg(not(feature = "no_index"))]
pub const FN_IDX_SET: &str = "index$set$"; pub const FN_IDX_SET: &str = "index$set$";
#[cfg(not(feature = "no_function"))]
pub const FN_ANONYMOUS: &str = "anon$"; pub const FN_ANONYMOUS: &str = "anon$";
pub const MARKER_EXPR: &str = "$expr$"; pub const MARKER_EXPR: &str = "$expr$";
pub const MARKER_BLOCK: &str = "$block$"; pub const MARKER_BLOCK: &str = "$block$";
pub const MARKER_IDENT: &str = "$ident$"; pub const MARKER_IDENT: &str = "$ident$";
/// A type specifying the method of chaining. /// A type specifying the method of chaining.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ChainType { pub enum ChainType {
None, None,
@ -100,6 +117,7 @@ pub enum ChainType {
} }
/// A type that encapsulates a mutation target for an expression with side effects. /// 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)] #[derive(Debug)]
pub enum Target<'a> { pub enum Target<'a> {
/// The target is a mutable reference to a `Dynamic` value somewhere. /// The target is a mutable reference to a `Dynamic` value somewhere.
@ -108,15 +126,19 @@ pub enum Target<'a> {
Value(Dynamic), Value(Dynamic),
/// The target is a character inside a String. /// The target is a character inside a String.
/// This is necessary because directly pointing to a char inside a String is impossible. /// 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), StringChar(&'a mut Dynamic, usize, Dynamic),
} }
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
impl Target<'_> { impl Target<'_> {
/// Is the `Target` a reference pointing to other data? /// Is the `Target` a reference pointing to other data?
pub fn is_ref(&self) -> bool { pub fn is_ref(&self) -> bool {
match self { match self {
Self::Ref(_) => true, 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? /// Is the `Target` an owned value?
@ -124,22 +146,26 @@ impl Target<'_> {
match self { match self {
Self::Ref(_) => false, Self::Ref(_) => false,
Self::Value(_) => true, Self::Value(_) => true,
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, _) => false, Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a specific type? /// Is the `Target` a specific type?
#[allow(dead_code)]
pub fn is<T: Variant + Clone>(&self) -> bool { pub fn is<T: Variant + Clone>(&self) -> bool {
match self { match self {
Target::Ref(r) => r.is::<T>(), Target::Ref(r) => r.is::<T>(),
Target::Value(r) => r.is::<T>(), Target::Value(r) => r.is::<T>(),
#[cfg(not(feature = "no_index"))]
Target::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(), Target::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(),
} }
} }
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
pub fn clone_into_dynamic(self) -> Dynamic { pub fn clone_into_dynamic(self) -> Dynamic {
match self { match self {
Self::Ref(r) => r.clone(), // Referenced value is cloned Self::Ref(r) => r.clone(), // Referenced value is cloned
Self::Value(v) => v, // Owned value is simply taken Self::Value(v) => v, // Owned value is simply taken
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => ch, // Character is taken Self::StringChar(_, _, ch) => ch, // Character is taken
} }
} }
@ -148,6 +174,7 @@ impl Target<'_> {
match self { match self {
Self::Ref(r) => *r, Self::Ref(r) => *r,
Self::Value(ref mut r) => r, Self::Value(ref mut r) => r,
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ref mut r) => r, Self::StringChar(_, _, ref mut r) => r,
} }
} }
@ -161,6 +188,7 @@ impl Target<'_> {
Position::none(), Position::none(),
))) )))
} }
#[cfg(not(feature = "no_index"))]
Self::StringChar(Dynamic(Union::Str(ref mut s)), index, _) => { Self::StringChar(Dynamic(Union::Str(ref mut s)), index, _) => {
// Replace the character at the specified index position // Replace the character at the specified index position
let new_ch = new_val let new_ch = new_val
@ -176,18 +204,21 @@ impl Target<'_> {
*s = chars.iter().collect::<String>().into(); *s = chars.iter().collect::<String>().into();
} }
} }
_ => unreachable!(), #[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, _) => unreachable!(),
} }
Ok(()) Ok(())
} }
} }
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
impl<'a> From<&'a mut Dynamic> for Target<'a> { impl<'a> From<&'a mut Dynamic> for Target<'a> {
fn from(value: &'a mut Dynamic) -> Self { fn from(value: &'a mut Dynamic) -> Self {
Self::Ref(value) Self::Ref(value)
} }
} }
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
impl<T: Into<Dynamic>> From<T> for Target<'_> { impl<T: Into<Dynamic>> From<T> for Target<'_> {
fn from(value: T) -> Self { fn from(value: T) -> Self {
Self::Value(value.into()) 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. /// Rhai main scripting engine.
/// ///
/// ``` /// ```
@ -275,6 +334,7 @@ pub struct Engine {
pub(crate) packages: PackagesCollection, pub(crate) packages: PackagesCollection,
/// A module resolution service. /// A module resolution service.
#[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Option<Box<dyn ModuleResolver>>, pub(crate) module_resolver: Option<Box<dyn ModuleResolver>>,
/// A hashmap mapping type names to pretty-print names. /// A hashmap mapping type names to pretty-print names.
@ -296,24 +356,10 @@ pub struct Engine {
/// Optimize the AST after compilation. /// Optimize the AST after compilation.
pub(crate) optimization_level: OptimizationLevel, pub(crate) optimization_level: OptimizationLevel,
/// Maximum levels of call-stack to prevent infinite recursion.
/// /// Max limits.
/// Defaults to 16 for debug builds and 128 for non-debug builds. #[cfg(not(feature = "unchecked"))]
pub(crate) max_call_stack_depth: usize, pub(crate) limits: Limits,
/// 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,
} }
impl fmt::Debug for Engine { impl fmt::Debug for Engine {
@ -338,7 +384,8 @@ impl Default for Engine {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())), 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, module_resolver: None,
type_names: None, type_names: None,
@ -360,14 +407,17 @@ impl Default for Engine {
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
optimization_level: OptimizationLevel::Simple, optimization_level: OptimizationLevel::Simple,
max_call_stack_depth: MAX_CALL_STACK_DEPTH, #[cfg(not(feature = "unchecked"))]
max_expr_depth: MAX_EXPR_DEPTH, limits: Limits {
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_operations: 0, max_expr_depth: MAX_EXPR_DEPTH,
max_modules: usize::MAX, max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
max_string_size: 0, max_operations: 0,
max_array_size: 0, max_modules: usize::MAX,
max_map_size: 0, max_string_size: 0,
max_array_size: 0,
max_map_size: 0,
},
}; };
engine.load_package(StandardPackage::new().get()); engine.load_package(StandardPackage::new().get());
@ -377,20 +427,24 @@ impl Default for Engine {
} }
/// Make getter function /// Make getter function
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn make_getter(id: &str) -> String { pub fn make_getter(id: &str) -> String {
format!("{}{}", FN_GET, id) format!("{}{}", FN_GET, id)
} }
/// Make setter function /// Make setter function
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn make_setter(id: &str) -> String { pub fn make_setter(id: &str) -> String {
format!("{}{}", FN_SET, id) format!("{}{}", FN_SET, id)
} }
/// Print/debug to stdout /// Print/debug to stdout
fn default_print(s: &str) { fn default_print(_s: &str) {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
println!("{}", s); println!("{}", _s);
} }
/// Search for a module within an imports stack. /// Search for a module within an imports stack.
@ -546,6 +600,8 @@ impl Engine {
packages: Default::default(), packages: Default::default(),
global_module: Default::default(), global_module: Default::default(),
#[cfg(not(feature = "no_module"))]
module_resolver: None, module_resolver: None,
type_names: None, type_names: None,
@ -563,19 +619,23 @@ impl Engine {
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
optimization_level: OptimizationLevel::Simple, optimization_level: OptimizationLevel::Simple,
max_call_stack_depth: MAX_CALL_STACK_DEPTH, #[cfg(not(feature = "unchecked"))]
max_expr_depth: MAX_EXPR_DEPTH, limits: Limits {
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_operations: 0, max_expr_depth: MAX_EXPR_DEPTH,
max_modules: usize::MAX, max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
max_string_size: 0, max_operations: 0,
max_array_size: 0, max_modules: usize::MAX,
max_map_size: 0, max_string_size: 0,
max_array_size: 0,
max_map_size: 0,
},
} }
} }
/// Chain-evaluate a dot/index chain. /// Chain-evaluate a dot/index chain.
/// Position in `EvalAltResult` is `None` and must be set afterwards. /// 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( fn eval_dot_index_chain_helper(
&self, &self,
state: &mut State, state: &mut State,
@ -586,7 +646,7 @@ impl Engine {
idx_values: &mut StaticVec<Dynamic>, idx_values: &mut StaticVec<Dynamic>,
chain_type: ChainType, chain_type: ChainType,
level: usize, level: usize,
mut new_val: Option<Dynamic>, mut _new_val: Option<Dynamic>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
if chain_type == ChainType::None { if chain_type == ChainType::None {
panic!(); panic!();
@ -618,18 +678,19 @@ impl Engine {
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, this_ptr, obj_ptr, expr, idx_values, next_chain, level, state, lib, this_ptr, obj_ptr, expr, idx_values, next_chain, level,
new_val, _new_val,
) )
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
} }
// xxx[rhs] = new_val // xxx[rhs] = new_val
_ if new_val.is_some() => { _ if _new_val.is_some() => {
let mut new_val = new_val.unwrap(); let mut new_val = _new_val.unwrap();
let mut idx_val2 = idx_val.clone(); let mut idx_val2 = idx_val.clone();
match self.get_indexed_mut(state, lib, target, idx_val, pos, true, level) { 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 // Indexed value is an owned value - the only possibility is an indexer
// Try to call an index setter // Try to call an index setter
#[cfg(not(feature = "no_index"))]
Ok(obj_ptr) if obj_ptr.is_value() => { Ok(obj_ptr) if obj_ptr.is_value() => {
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val]; let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
@ -639,9 +700,7 @@ impl Engine {
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// If there is no index setter, no need to set it back because the indexer is read-only // If there is no index setter, no need to set it back because the indexer is read-only
EvalAltResult::ErrorFunctionNotFound(s, _) EvalAltResult::ErrorFunctionNotFound(_, _) => {
if s == FN_IDX_SET =>
{
Ok(Default::default()) Ok(Default::default())
} }
_ => Err(err), _ => Err(err),
@ -655,6 +714,7 @@ impl Engine {
} }
Err(err) => match *err { Err(err) => match *err {
// No index getter - try to call an index setter // No index getter - try to call an index setter
#[cfg(not(feature = "no_index"))]
EvalAltResult::ErrorIndexingType(_, _) => { EvalAltResult::ErrorIndexingType(_, _) => {
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val]; let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
@ -686,13 +746,13 @@ impl Engine {
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_) => unreachable!(),
// {xxx:map}.id = ??? // {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 ((prop, _, _), pos) = x.as_ref();
let index = prop.clone().into(); let index = prop.clone().into();
let mut val = let mut val =
self.get_indexed_mut(state, lib, target, index, *pos, true, level)?; 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()))?; .map_err(|err| err.new_position(rhs.position()))?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
@ -706,9 +766,9 @@ impl Engine {
Ok((val.clone_into_dynamic(), false)) Ok((val.clone_into_dynamic(), false))
} }
// xxx.id = ??? // 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 ((_, _, 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( self.exec_fn_call(
state, lib, setter, true, 0, &mut args, is_ref, true, None, level, state, lib, setter, true, 0, &mut args, is_ref, true, None, level,
) )
@ -750,7 +810,7 @@ impl Engine {
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, this_ptr, &mut val, expr, idx_values, next_chain, level, state, lib, this_ptr, &mut val, expr, idx_values, next_chain, level,
new_val, _new_val,
) )
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
} }
@ -777,7 +837,7 @@ impl Engine {
let (result, may_be_changed) = self let (result, may_be_changed) = self
.eval_dot_index_chain_helper( .eval_dot_index_chain_helper(
state, lib, this_ptr, target, expr, idx_values, next_chain, state, lib, this_ptr, target, expr, idx_values, next_chain,
level, new_val, level, _new_val,
) )
.map_err(|err| err.new_position(*pos))?; .map_err(|err| err.new_position(*pos))?;
@ -812,7 +872,7 @@ impl Engine {
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, this_ptr, target, expr, idx_values, next_chain, state, lib, this_ptr, target, expr, idx_values, next_chain,
level, new_val, level, _new_val,
) )
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
} }
@ -835,6 +895,7 @@ impl Engine {
} }
/// Evaluate a dot/index chain. /// Evaluate a dot/index chain.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn eval_dot_index_chain( fn eval_dot_index_chain(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -911,6 +972,7 @@ impl Engine {
/// Any spill-overs are stored in `more`, which is dynamic. /// 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 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. /// The total number of values is returned.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn eval_indexed_chain( fn eval_indexed_chain(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -981,26 +1043,30 @@ impl Engine {
/// Get the value at the indexed position of a base type /// Get the value at the indexed position of a base type
/// Position in `EvalAltResult` may be None and should be set afterwards. /// 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>( fn get_indexed_mut<'a>(
&self, &self,
state: &mut State, state: &mut State,
lib: &Module, _lib: &Module,
target: &'a mut Target, target: &'a mut Target,
mut idx: Dynamic, mut _idx: Dynamic,
idx_pos: Position, idx_pos: Position,
create: bool, _create: bool,
level: usize, _level: usize,
) -> Result<Target<'a>, Box<EvalAltResult>> { ) -> Result<Target<'a>, Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state)?;
#[cfg(not(feature = "no_index"))]
#[cfg(not(feature = "no_object"))]
let is_ref = target.is_ref(); let is_ref = target.is_ref();
let val = target.as_mut(); let val = target.as_mut();
match val { match val {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(arr)) => { Dynamic(Union::Array(arr)) => {
// val_array[idx] // val_array[idx]
let index = idx let index = _idx
.as_int() .as_int()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_pos))?; .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_pos))?;
@ -1022,14 +1088,14 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map)) => { Dynamic(Union::Map(map)) => {
// val_map[idx] // val_map[idx]
Ok(if create { Ok(if _create {
let index = idx let index = _idx
.take_immutable_string() .take_immutable_string()
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_pos))?; .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
map.entry(index).or_insert(Default::default()).into() map.entry(index).or_insert(Default::default()).into()
} else { } else {
let index = idx let index = _idx
.downcast_ref::<ImmutableString>() .downcast_ref::<ImmutableString>()
.ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?; .ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
@ -1043,7 +1109,7 @@ impl Engine {
Dynamic(Union::Str(s)) => { Dynamic(Union::Str(s)) => {
// val_string[idx] // val_string[idx]
let chars_len = s.chars().count(); let chars_len = s.chars().count();
let index = idx let index = _idx
.as_int() .as_int()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_pos))?; .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_pos))?;
@ -1064,9 +1130,9 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
_ => { _ => {
let type_name = val.type_name(); let type_name = val.type_name();
let args = &mut [val, &mut idx]; let args = &mut [val, &mut _idx];
self.exec_fn_call( 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(|(v, _)| v.into())
.map_err(|err| match *err { .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( _ => Err(Box::new(EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).into(), self.map_type_name(val.type_name()).into(),
Position::none(), Position::none(),
@ -1254,7 +1320,7 @@ impl Engine {
let mut rhs_val = let mut rhs_val =
self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?; 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 // Normal assignment
rhs_val rhs_val
} else { } else {
@ -1277,7 +1343,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_) => { Expr::Index(_) => {
self.eval_dot_index_chain( 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()) Ok(Default::default())
} }
@ -1285,7 +1351,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_) => { Expr::Dot(_) => {
self.eval_dot_index_chain( 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()) Ok(Default::default())
} }
@ -1641,19 +1707,20 @@ impl Engine {
Stmt::Const(_) => unreachable!(), Stmt::Const(_) => unreachable!(),
// Import statement // Import statement
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => { Stmt::Import(x) => {
let (expr, (name, pos)) = x.as_ref(); let (expr, (name, _pos)) = x.as_ref();
// Guard against too many modules // Guard against too many modules
if state.modules >= self.max_modules { #[cfg(not(feature = "unchecked"))]
return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos))); if state.modules >= self.limits.max_modules {
return Err(Box::new(EvalAltResult::ErrorTooManyModules(*_pos)));
} }
if let Some(path) = self if let Some(path) = self
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
.try_cast::<ImmutableString>() .try_cast::<ImmutableString>()
{ {
#[cfg(not(feature = "no_module"))]
if let Some(resolver) = &self.module_resolver { if let Some(resolver) = &self.module_resolver {
let mut module = resolver.resolve(self, &path, expr.position())?; let mut module = resolver.resolve(self, &path, expr.position())?;
module.index_all_sub_modules(); module.index_all_sub_modules();
@ -1668,15 +1735,13 @@ impl Engine {
expr.position(), expr.position(),
))) )))
} }
#[cfg(feature = "no_module")]
Ok(Default::default())
} else { } else {
Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position())))
} }
} }
// Export statement // Export statement
#[cfg(not(feature = "no_module"))]
Stmt::Export(list) => { Stmt::Export(list) => {
for ((id, id_pos), rename) in list.iter() { for ((id, id_pos), rename) in list.iter() {
// Mark scope variables as public // Mark scope variables as public
@ -1698,8 +1763,18 @@ impl Engine {
.map_err(|err| err.new_position(stmt.position())) .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. /// Check a result to ensure that the data size is within allowable limit.
/// Position in `EvalAltResult` may be None and should be set afterwards. /// Position in `EvalAltResult` may be None and should be set afterwards.
#[cfg(not(feature = "unchecked"))]
fn check_data_size( fn check_data_size(
&self, &self,
result: Result<Dynamic, Box<EvalAltResult>>, result: Result<Dynamic, Box<EvalAltResult>>,
@ -1708,7 +1783,8 @@ impl Engine {
return result; return result;
// If no data size limits, just return // 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; return result;
} }
@ -1768,37 +1844,37 @@ impl Engine {
// Simply return all errors // Simply return all errors
Err(_) => return result, Err(_) => return result,
// String with limit // 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 // Array with limit
#[cfg(not(feature = "no_index"))] #[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 // Map with limit
#[cfg(not(feature = "no_object"))] #[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 // Everything else is simply returned
Ok(_) => return result, Ok(_) => return result,
}; };
let (arr, map, s) = calc_size(result.as_ref().unwrap()); 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( Err(Box::new(EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
self.max_string_size, self.limits.max_string_size,
s, s,
Position::none(), Position::none(),
))) )))
} else if arr > self.max_array_size { } else if arr > self.limits.max_array_size {
Err(Box::new(EvalAltResult::ErrorDataTooLarge( Err(Box::new(EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(), "Size of array".to_string(),
self.max_array_size, self.limits.max_array_size,
arr, arr,
Position::none(), Position::none(),
))) )))
} else if map > self.max_map_size { } else if map > self.limits.max_map_size {
Err(Box::new(EvalAltResult::ErrorDataTooLarge( Err(Box::new(EvalAltResult::ErrorDataTooLarge(
"Number of properties in object map".to_string(), "Number of properties in object map".to_string(),
self.max_map_size, self.limits.max_map_size,
map, map,
Position::none(), Position::none(),
))) )))
@ -1814,7 +1890,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
// Guard against too many operations // 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( return Err(Box::new(EvalAltResult::ErrorTooManyOperations(
Position::none(), Position::none(),
))); )));

View File

@ -22,11 +22,10 @@ macro_rules! impl_args {
fn into_vec(self) -> StaticVec<Dynamic> { fn into_vec(self) -> StaticVec<Dynamic> {
let ($($p,)*) = self; let ($($p,)*) = self;
#[allow(unused_mut)] let mut _v = StaticVec::new();
let mut v = StaticVec::new(); $(_v.push($p.into_dynamic());)*
$(v.push($p.into_dynamic());)*
v _v
} }
} }

View File

@ -3,26 +3,34 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{ use crate::engine::{
search_imports, search_namespace, search_scope_only, Engine, Imports, State, Target, FN_GET, search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
FN_IDX_GET, FN_IDX_SET, FN_SET, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_PRINT,
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_PRINT, KEYWORD_TYPE_OF, KEYWORD_TYPE_OF,
}; };
use crate::error::ParseErrorType; use crate::error::ParseErrorType;
use crate::fn_native::{FnCallArgs, FnPtr}; use crate::fn_native::{FnCallArgs, FnPtr};
use crate::module::{Module, ModuleRef}; use crate::module::{Module, ModuleRef};
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::parser::{Expr, ImmutableString, ScriptFnDef, AST, INT}; use crate::parser::{Expr, ImmutableString, AST, INT};
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::Scope;
use crate::token::Position; use crate::token::Position;
use crate::utils::StaticVec; 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"))] #[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT; use crate::parser::FLOAT;
#[cfg(not(feature = "no_index"))]
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::engine::Map; use crate::engine::{Map, Target, FN_GET, FN_SET};
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
@ -36,20 +44,22 @@ use crate::stdlib::{
}; };
/// Extract the property name from a getter function name. /// 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"))] #[cfg(not(feature = "no_object"))]
if fn_name.starts_with(FN_GET) { if _fn_name.starts_with(FN_GET) {
return Some(&fn_name[FN_GET.len()..]); return Some(&_fn_name[FN_GET.len()..]);
} }
None None
} }
/// Extract the property name from a setter function name. /// 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"))] #[cfg(not(feature = "no_object"))]
if fn_name.starts_with(FN_SET) { if _fn_name.starts_with(FN_SET) {
return Some(&fn_name[FN_SET.len()..]); return Some(&_fn_name[FN_SET.len()..]);
} }
None 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 `()`! /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
pub(crate) fn call_fn_raw( pub(crate) fn call_fn_raw(
&self, &self,
scope: &mut Scope, _scope: &mut Scope,
mods: &mut Imports, _mods: &mut Imports,
state: &mut State, state: &mut State,
lib: &Module, lib: &Module,
fn_name: &str, fn_name: &str,
(hash_fn, hash_script): (u64, u64), (hash_fn, hash_script): (u64, u64),
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
is_method: bool, _is_method: bool,
def_val: Option<bool>, def_val: Option<bool>,
level: usize, _level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state)?;
@ -125,7 +135,7 @@ impl Engine {
// Check for stack overflow // Check for stack overflow
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if level > self.max_call_stack_depth { if _level > self.limits.max_call_stack_depth {
return Err(Box::new( return Err(Box::new(
EvalAltResult::ErrorStackOverflow(Position::none()), EvalAltResult::ErrorStackOverflow(Position::none()),
)); ));
@ -151,7 +161,7 @@ impl Engine {
if let Some(func) = func { if let Some(func) = func {
#[cfg(not(feature = "no_function"))] #[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")] #[cfg(feature = "no_function")]
let need_normalize = is_ref && func.is_pure(); let need_normalize = is_ref && func.is_pure();
@ -164,25 +174,25 @@ impl Engine {
let fn_def = func.get_fn_def(); let fn_def = func.get_fn_def();
// Method call of script function - map first argument to `this` // 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); let (first, rest) = args.split_at_mut(1);
Ok(( Ok((
self.call_script_fn( self.call_script_fn(
scope, _scope,
mods, _mods,
state, state,
lib, lib,
&mut Some(first[0]), &mut Some(first[0]),
fn_name, fn_name,
fn_def, fn_def,
rest, rest,
level, _level,
)?, )?,
false, false,
)) ))
} else { } else {
let result = self.call_script_fn( 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 // Restore the original reference
@ -256,6 +266,7 @@ impl Engine {
} }
// index getter function not found? // index getter function not found?
#[cfg(not(feature = "no_index"))]
if fn_name == FN_IDX_GET && args.len() == 2 { if fn_name == FN_IDX_GET && args.len() == 2 {
return Err(Box::new(EvalAltResult::ErrorFunctionNotFound( return Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
format!( format!(
@ -268,6 +279,7 @@ impl Engine {
} }
// index setter function not found? // index setter function not found?
#[cfg(not(feature = "no_index"))]
if fn_name == FN_IDX_SET { if fn_name == FN_IDX_SET {
return Err(Box::new(EvalAltResult::ErrorFunctionNotFound( return Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
format!( format!(
@ -305,6 +317,7 @@ impl Engine {
/// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// 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. /// 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 `()`! /// **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( pub(crate) fn call_script_fn(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -486,6 +499,7 @@ impl Engine {
} }
/// Call a dot method. /// Call a dot method.
#[cfg(not(feature = "no_object"))]
pub(crate) fn make_method_call( pub(crate) fn make_method_call(
&self, &self,
state: &mut State, state: &mut State,
@ -506,9 +520,9 @@ impl Engine {
// Get a reference to the mutation target Dynamic // Get a reference to the mutation target Dynamic
let obj = target.as_mut(); let obj = target.as_mut();
let mut idx = idx_val.cast::<StaticVec<Dynamic>>(); 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 // FnPtr call
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap(); let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>(); let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
@ -527,7 +541,7 @@ impl Engine {
self.exec_fn_call( self.exec_fn_call(
state, lib, fn_name, *native, hash, args, false, false, *def_val, level, 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 // FnPtr call on object
let fn_ptr = idx.remove(0).cast::<FnPtr>(); let fn_ptr = idx.remove(0).cast::<FnPtr>();
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>(); let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
@ -546,7 +560,7 @@ impl Engine {
self.exec_fn_call( 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,
) )
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() { } else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
// Curry call // Curry call
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap(); let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
Ok(( Ok((
@ -563,19 +577,20 @@ impl Engine {
false, false,
)) ))
} else { } else {
#[cfg(not(feature = "no_object"))]
let redirected; let redirected;
let mut hash = *hash; let mut _hash = *hash;
// Check if it is a map method call in OOP style // Check if it is a map method call in OOP style
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if let Some(map) = obj.downcast_ref::<Map>() { 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>() { if let Some(f) = val.downcast_ref::<FnPtr>() {
// Remap the function name // Remap the function name
redirected = f.get_fn_name().clone(); redirected = f.get_fn_name().clone();
fn_name = &redirected; _fn_name = &redirected;
// Recalculate the hash based on the new function name // 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(); let args = arg_values.as_mut();
self.exec_fn_call( 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))?; .map_err(|err| err.new_position(*pos))?;
@ -864,10 +879,22 @@ impl Engine {
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => { EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => {
Ok(def_val.unwrap().into()) Ok(def_val.unwrap().into())
} }
EvalAltResult::ErrorFunctionNotFound(_, _) => { EvalAltResult::ErrorFunctionNotFound(_, pos) => {
Err(Box::new(EvalAltResult::ErrorFunctionNotFound( Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
format!("{}{}", modules, name), format!(
Position::none(), "{}{} ({})",
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), _ => Err(err),

View File

@ -2,16 +2,23 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::engine::Engine; use crate::engine::Engine;
use crate::module::{FuncReturn, Module}; use crate::module::Module;
use crate::parser::ScriptFnDef;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::{is_valid_identifier, Position}; use crate::token::{is_valid_identifier, Position};
use crate::utils::{ImmutableString, StaticVec}; use crate::utils::ImmutableString;
use crate::Scope;
use crate::stdlib::{ #[cfg(not(feature = "no_function"))]
boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc, vec::Vec, 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. /// Trait that maps to `Send + Sync` only under the `sync` feature.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
@ -82,12 +89,17 @@ impl FnPtr {
/// Call the function pointer with curried arguments (if any). /// 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 /// ## WARNING
/// ///
/// All the arguments are _consumed_, meaning that they're replaced by `()`. /// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments. /// This is to avoid unnecessarily cloning the arguments.
/// Do not use the arguments after this call. If they are needed afterwards, /// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
#[cfg(not(feature = "no_function"))]
pub fn call_dynamic( pub fn call_dynamic(
&self, &self,
engine: &Engine, engine: &Engine,

View File

@ -133,12 +133,11 @@ macro_rules! make_func {
Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| { Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types! // 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). // 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. // 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 // 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")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[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")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[deprecated(note = "this type is volatile and may change")]

View File

@ -2,22 +2,33 @@
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{make_getter, make_setter, Engine, Imports, FN_IDX_GET, FN_IDX_SET}; use crate::engine::Engine;
use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync, Shared}; use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync};
use crate::parser::{ use crate::parser::{FnAccess, FnAccess::Public};
FnAccess,
FnAccess::{Private, Public},
ScriptFnDef, AST,
};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, Scope};
use crate::token::{Position, Token}; use crate::token::{Position, Token};
use crate::utils::{StaticVec, StraightHasherBuilder}; 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::{ use crate::stdlib::{
any::TypeId, any::TypeId,
boxed::Box, boxed::Box,
cell::RefCell,
collections::HashMap, collections::HashMap,
fmt, format, fmt, format,
iter::empty, iter::empty,
@ -25,11 +36,16 @@ use crate::stdlib::{
num::NonZeroUsize, num::NonZeroUsize,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
string::{String, ToString}, string::{String, ToString},
vec,
vec::Vec, vec::Vec,
}; };
#[cfg(not(feature = "no_std"))] #[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")] #[cfg(feature = "sync")]
use crate::stdlib::sync::RwLock; use crate::stdlib::sync::RwLock;
@ -738,6 +754,8 @@ impl Module {
/// }); /// });
/// assert!(module.contains_fn(hash)); /// 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>( pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
&mut self, &mut self,
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static, 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_get));
/// assert!(module.contains_fn(hash_set)); /// 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>( pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self, &mut self,
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static, getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
@ -909,11 +929,11 @@ impl Module {
self.merge_filtered(other, |_, _, _| true) 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( pub(crate) fn merge_filtered(
&mut self, &mut self,
other: &Self, other: &Self,
filter: impl Fn(FnAccess, &str, usize) -> bool, _filter: impl Fn(FnAccess, &str, usize) -> bool,
) -> &mut Self { ) -> &mut Self {
self.variables self.variables
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone()))); .extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
@ -924,7 +944,7 @@ impl Module {
.iter() .iter()
.filter(|(_, (_, _, _, v))| match v { .filter(|(_, (_, _, _, v))| match v {
#[cfg(not(feature = "no_function"))] #[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, _ => true,
}) })
.map(|(&k, v)| (k, v.clone())), .map(|(&k, v)| (k, v.clone())),
@ -975,6 +995,7 @@ impl Module {
} }
/// Get an iterator to the functions in the module. /// Get an iterator to the functions in the module.
#[cfg(not(feature = "no_function"))]
pub(crate) fn iter_fn( pub(crate) fn iter_fn(
&self, &self,
) -> impl Iterator<Item = &(String, FnAccess, StaticVec<TypeId>, Func)> { ) -> 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 /// Scan through all the sub-modules in the module build an index of all
/// variables and external Rust functions via hashing. /// variables and external Rust functions via hashing.
#[cfg(not(feature = "no_module"))]
pub(crate) fn index_all_sub_modules(&mut self) { pub(crate) fn index_all_sub_modules(&mut self) {
// Collect a particular module. // Collect a particular module.
fn index_module<'a>( fn index_module<'a>(
@ -1063,8 +1085,8 @@ impl Module {
for (name, access, params, func) in module.functions.values() { for (name, access, params, func) in module.functions.values() {
match access { match access {
// Private functions are not exported // Private functions are not exported
Private => continue, FnAccess::Private => continue,
Public => (), FnAccess::Public => (),
} }
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -1100,10 +1122,13 @@ impl Module {
return; return;
} }
let mut variables = Vec::new(); let mut qualifiers: Vec<_> = Default::default();
let mut functions = Vec::new(); 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_variables = variables.into_iter().collect();
self.all_functions = functions.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, Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
}; };
use crate::module::Module; 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::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::token::is_valid_identifier; use crate::token::is_valid_identifier;
use crate::utils::StaticVec; use crate::utils::StaticVec;
#[cfg(not(feature = "no_function"))]
use crate::parser::ReturnType;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
use crate::parser::CustomExpr; use crate::parser::CustomExpr;
@ -249,6 +252,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
// let id; // let id;
stmt @ Stmt::Let(_) => stmt, stmt @ Stmt::Let(_) => stmt,
// import expr as id; // import expr as id;
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))), Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
// { block } // { block }
Stmt::Block(x) => { Stmt::Block(x) => {
@ -290,6 +294,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
match expr { match expr {
Stmt::Let(x) if x.1.is_none() => removed = true, Stmt::Let(x) if x.1.is_none() => removed = true,
Stmt::Let(x) if x.1.is_some() => removed = x.1.unwrap().is_pure(), 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(), Stmt::Import(x) => removed = x.0.is_pure(),
_ => { _ => {
result.push(expr); result.push(expr);
@ -345,8 +350,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
state.set_dirty(); state.set_dirty();
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// Only one let/import statement - leave it alone // Only one let statement - leave it alone
[Stmt::Let(_)] | [Stmt::Import(_)] => Stmt::Block(Box::new((result.into(), pos))), [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 // Only one statement - promote
[_] => { [_] => {
state.set_dirty(); 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) // First search in functions lib (can override built-in)
// Cater for both normal function call style and method call style (one additional arguments) // Cater for both normal function call style and method call style (one additional arguments)
#[cfg(not(feature = "no_function"))] #[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; } if !f.is_script() { return false; }
let fn_def = f.get_fn_def(); let fn_def = f.get_fn_def();
fn_def.name.as_str() == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len()) fn_def.name.as_str() == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
}).is_some(); }).is_some();
#[cfg(feature = "no_function")] #[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 // 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(); x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
return Expr::FnCall(x); return Expr::FnCall(x);
@ -686,7 +694,9 @@ fn optimize(
// Keep all variable declarations at this level // Keep all variable declarations at this level
// and always keep the last return value // and always keep the last return value
let keep = match stmt { let keep = match stmt {
Stmt::Let(_) | Stmt::Import(_) => true, Stmt::Let(_) => true,
#[cfg(not(feature = "no_module"))]
Stmt::Import(_) => true,
_ => i == num_statements - 1, _ => i == num_statements - 1,
}; };
optimize_stmt(stmt, &mut state, keep) optimize_stmt(stmt, &mut state, keep)
@ -721,7 +731,7 @@ pub fn optimize_into_ast(
engine: &Engine, engine: &Engine,
scope: &Scope, scope: &Scope,
statements: Vec<Stmt>, statements: Vec<Stmt>,
functions: Vec<ScriptFnDef>, _functions: Vec<ScriptFnDef>,
level: OptimizationLevel, level: OptimizationLevel,
) -> AST { ) -> AST {
#[cfg(feature = "no_optimize")] #[cfg(feature = "no_optimize")]
@ -735,7 +745,7 @@ pub fn optimize_into_ast(
// We only need the script library's signatures for optimization purposes // We only need the script library's signatures for optimization purposes
let mut lib2 = Module::new(); let mut lib2 = Module::new();
functions _functions
.iter() .iter()
.map(|fn_def| { .map(|fn_def| {
ScriptFnDef { ScriptFnDef {
@ -751,7 +761,7 @@ pub fn optimize_into_ast(
lib2.set_script_fn(fn_def); lib2.set_script_fn(fn_def);
}); });
functions _functions
.into_iter() .into_iter()
.map(|mut fn_def| { .map(|mut fn_def| {
let pos = fn_def.body.position(); let pos = fn_def.body.position();
@ -782,7 +792,7 @@ pub fn optimize_into_ast(
module.set_script_fn(fn_def); module.set_script_fn(fn_def);
}); });
} else { } else {
functions.into_iter().for_each(|fn_def| { _functions.into_iter().for_each(|fn_def| {
module.set_script_fn(fn_def); module.set_script_fn(fn_def);
}); });
} }

View File

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

View File

@ -5,10 +5,14 @@ use crate::def_package;
use crate::engine::{Array, Engine}; use crate::engine::{Array, Engine};
use crate::module::{FuncReturn, Module}; use crate::module::{FuncReturn, Module};
use crate::parser::{ImmutableString, INT}; 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 // Register array utility functions
fn push<T: Variant + Clone>(list: &mut Array, item: T) -> FuncReturn<()> { 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(()) Ok(())
} }
fn pad<T: Variant + Clone>( fn pad<T: Variant + Clone>(
engine: &Engine, _engine: &Engine,
_: &Module, _: &Module,
args: &mut [&mut Dynamic], args: &mut [&mut Dynamic],
) -> FuncReturn<()> { ) -> FuncReturn<()> {
@ -34,10 +38,13 @@ fn pad<T: Variant + Clone>(
// Check if array will be over max size limit // Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))] #[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( return Err(Box::new(EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(), "Size of array".to_string(),
engine.max_array_size, _engine.limits.max_array_size,
len as usize, len as usize,
Position::none(), Position::none(),
))); )));

View File

@ -1,16 +1,20 @@
#![cfg(not(feature = "no_object"))] #![cfg(not(feature = "no_object"))]
use crate::any::Dynamic;
use crate::def_package; use crate::def_package;
use crate::engine::Map; use crate::engine::Map;
use crate::module::FuncReturn;
use crate::parser::{ImmutableString, INT}; 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; use crate::stdlib::vec::Vec;
#[cfg(not(feature = "no_index"))]
fn map_get_keys(map: &mut Map) -> FuncReturn<Vec<Dynamic>> { fn map_get_keys(map: &mut Map) -> FuncReturn<Vec<Dynamic>> {
Ok(map.iter().map(|(k, _)| k.clone().into()).collect()) Ok(map.iter().map(|(k, _)| k.clone().into()).collect())
} }
#[cfg(not(feature = "no_index"))]
fn map_get_values(map: &mut Map) -> FuncReturn<Vec<Dynamic>> { fn map_get_values(map: &mut Map) -> FuncReturn<Vec<Dynamic>> {
Ok(map.iter().map(|(_, v)| v.clone()).collect()) Ok(map.iter().map(|(_, v)| v.clone()).collect())
} }

View File

@ -1,20 +1,26 @@
use crate::def_package; use crate::def_package;
use crate::parser::INT; use crate::parser::INT;
use crate::result::EvalAltResult;
use crate::token::Position;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::parser::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(not(feature = "no_float"))]
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use num_traits::*; 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(feature = "only_i32")]
#[cfg(not(feature = "unchecked"))]
pub const MAX_INT: INT = i32::MAX; pub const MAX_INT: INT = i32::MAX;
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "unchecked"))]
pub const MAX_INT: INT = i64::MAX; pub const MAX_INT: INT = i64::MAX;
def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {

View File

@ -24,7 +24,6 @@ pub use arithmetic::ArithmeticPackage;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub use array_basic::BasicArrayPackage; pub use array_basic::BasicArrayPackage;
pub use eval::EvalPackage; pub use eval::EvalPackage;
#[cfg(not(feature = "no_function"))]
pub use fn_basic::BasicFnPackage; pub use fn_basic::BasicFnPackage;
pub use iter_basic::BasicIteratorPackage; pub use iter_basic::BasicIteratorPackage;
pub use logic::LogicPackage; pub use logic::LogicPackage;
@ -74,6 +73,7 @@ impl PackagesCollection {
.flatten() .flatten()
} }
/// Does the specified TypeId iterator exist in the `PackagesCollection`? /// Does the specified TypeId iterator exist in the `PackagesCollection`?
#[allow(dead_code)]
pub fn contains_iter(&self, id: TypeId) -> bool { pub fn contains_iter(&self, id: TypeId) -> bool {
self.0.iter().any(|p| p.contains_iter(id)) 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::engine::Engine;
use crate::module::{FuncReturn, Module}; use crate::module::{FuncReturn, Module};
use crate::parser::{ImmutableString, INT}; use crate::parser::{ImmutableString, INT};
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::utils::StaticVec; use crate::utils::StaticVec;
#[cfg(not(feature = "unchecked"))]
use crate::{result::EvalAltResult, token::Position};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::engine::Array; use crate::engine::Array;
@ -226,15 +227,15 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
lib.set_raw_fn( lib.set_raw_fn(
"pad", "pad",
&[TypeId::of::<ImmutableString>(), TypeId::of::<INT>(), TypeId::of::<char>()], &[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(); let len = *args[1].downcast_ref::< INT>().unwrap();
// Check if string will be over max size limit // Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))] #[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( return Err(Box::new(EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
engine.max_string_size, _engine.limits.max_string_size,
len as usize, len as usize,
Position::none(), Position::none(),
))); )));
@ -253,10 +254,11 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
p.push(ch); 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( return Err(Box::new(EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
engine.max_string_size, _engine.limits.max_string_size,
s.len(), s.len(),
Position::none(), Position::none(),
))); )));

View File

@ -1,11 +1,21 @@
#![cfg(not(feature = "no_std"))] #![cfg(not(feature = "no_std"))]
use super::logic::{eq, gt, gte, lt, lte, ne}; use super::logic::{eq, gt, gte, lt, lte, ne};
#[cfg(feature = "no_float")]
#[cfg(not(feature = "unchecked"))]
use super::math_basic::MAX_INT; use super::math_basic::MAX_INT;
use crate::def_package; use crate::def_package;
use crate::module::FuncReturn;
use crate::parser::INT;
use crate::result::EvalAltResult; 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; use crate::token::Position;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -14,9 +24,6 @@ use crate::stdlib::time::Instant;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use instant::Instant; use instant::Instant;
#[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT;
def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
// Register date/time functions // Register date/time functions
lib.set_fn_0("timestamp", || Ok(Instant::now())); lib.set_fn_0("timestamp", || Ok(Instant::now()));

View File

@ -2,10 +2,7 @@
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{ use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
make_getter, make_setter, Engine, FN_ANONYMOUS, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR,
MARKER_IDENT,
};
use crate::error::{LexError, ParseError, ParseErrorType}; use crate::error::{LexError, ParseError, ParseErrorType};
use crate::fn_native::Shared; use crate::fn_native::Shared;
use crate::module::{Module, ModuleRef}; use crate::module::{Module, ModuleRef};
@ -15,6 +12,12 @@ use crate::syntax::FnCustomSyntaxEval;
use crate::token::{is_valid_identifier, Position, Token, TokenStream}; use crate::token::{is_valid_identifier, Position, Token, TokenStream};
use crate::utils::{StaticVec, StraightHasherBuilder}; 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::{ use crate::stdlib::{
borrow::Cow, borrow::Cow,
boxed::Box, boxed::Box,
@ -32,9 +35,11 @@ use crate::stdlib::{
}; };
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(feature = "no_function"))]
use crate::stdlib::collections::hash_map::DefaultHasher; use crate::stdlib::collections::hash_map::DefaultHasher;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
#[cfg(not(feature = "no_function"))]
use ahash::AHasher; use ahash::AHasher;
/// The system integer type. /// The system integer type.
@ -84,7 +89,7 @@ impl AST {
&self.0 &self.0
} }
/// Get the statements. /// [INTERNALS] Get the statements.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this method is volatile and may change")] #[deprecated(note = "this method is volatile and may change")]
pub fn statements(&self) -> &[Stmt] { pub fn statements(&self) -> &[Stmt] {
@ -102,7 +107,7 @@ impl AST {
&self.1 &self.1
} }
/// Get the internal `Module` containing all script-defined functions. /// [INTERNALS] Get the internal `Module` containing all script-defined functions.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this method is volatile and may change")] #[deprecated(note = "this method is volatile and may change")]
pub fn lib(&self) -> &Module { 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. /// Encapsulates a local stack with variable names to simulate an actual runtime scope.
modules: Vec<String>, modules: Vec<String>,
/// Maximum levels of expression nesting. /// Maximum levels of expression nesting.
#[cfg(not(feature = "unchecked"))]
max_expr_depth: usize, max_expr_depth: usize,
/// Maximum levels of expression nesting in functions. /// Maximum levels of expression nesting in functions.
#[cfg(not(feature = "unchecked"))]
max_function_expr_depth: usize, max_function_expr_depth: usize,
} }
impl<'e> ParseState<'e> { impl<'e> ParseState<'e> {
/// Create a new `ParseState`. /// 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 { Self {
engine, engine,
max_expr_depth,
max_function_expr_depth,
stack: Default::default(), stack: Default::default(),
modules: 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. /// 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. /// 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> { pub fn ensure_level_within_max_limit(&self, limit: usize) -> Result<(), ParseError> {
if limit == 0 { if limit == 0 {
Ok(()) Ok(())
@ -519,8 +533,10 @@ pub enum Stmt {
/// return/throw /// return/throw
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>)>), ReturnWithVal(Box<((ReturnType, Position), Option<Expr>)>),
/// import expr as module /// import expr as module
#[cfg(not(feature = "no_module"))]
Import(Box<(Expr, (String, Position))>), Import(Box<(Expr, (String, Position))>),
/// expr id as name, ... /// expr id as name, ...
#[cfg(not(feature = "no_module"))]
Export(Box<StaticVec<((String, Position), Option<(String, Position)>)>>), Export(Box<StaticVec<((String, Position), Option<(String, Position)>)>>),
} }
@ -544,7 +560,10 @@ impl Stmt {
Stmt::While(x) => x.1.position(), Stmt::While(x) => x.1.position(),
Stmt::Loop(x) => x.position(), Stmt::Loop(x) => x.position(),
Stmt::For(x) => x.2.position(), Stmt::For(x) => x.2.position(),
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => (x.1).1, Stmt::Import(x) => (x.1).1,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => (x.get(0).0).1, Stmt::Export(x) => (x.get(0).0).1,
} }
} }
@ -563,12 +582,13 @@ impl Stmt {
Stmt::Let(_) Stmt::Let(_)
| Stmt::Const(_) | Stmt::Const(_)
| Stmt::Import(_)
| Stmt::Export(_)
| Stmt::Expr(_) | Stmt::Expr(_)
| Stmt::Continue(_) | Stmt::Continue(_)
| Stmt::Break(_) | Stmt::Break(_)
| Stmt::ReturnWithVal(_) => false, | 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::Let(_) | Stmt::Const(_) => false,
Stmt::Block(x) => x.0.iter().all(Stmt::is_pure), Stmt::Block(x) => x.0.iter().all(Stmt::is_pure),
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_) => false, Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_) => false,
#[cfg(not(feature = "no_module"))]
Stmt::Import(_) => false, Stmt::Import(_) => false,
#[cfg(not(feature = "no_module"))]
Stmt::Export(_) => false, Stmt::Export(_) => false,
} }
} }
@ -950,6 +973,7 @@ impl Expr {
} }
/// Convert a `Variable` into a `Property`. All other variants are untouched. /// Convert a `Variable` into a `Property`. All other variants are untouched.
#[cfg(not(feature = "no_object"))]
pub(crate) fn into_property(self) -> Self { pub(crate) fn into_property(self) -> Self {
match self { match self {
Self::Variable(x) if x.1.is_none() => { Self::Variable(x) if x.1.is_none() => {
@ -996,6 +1020,7 @@ fn parse_paren_expr(
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
if match_token(input, Token::RightParen)? { if match_token(input, Token::RightParen)? {
@ -1028,6 +1053,8 @@ fn parse_call_expr(
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut args = StaticVec::new(); let mut args = StaticVec::new();
@ -1141,6 +1168,7 @@ fn parse_call_expr(
/// Parse an indexing chain. /// Parse an indexing chain.
/// Indexing binds to the right, so this call parses all possible levels of indexing following in the input. /// 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( fn parse_index_chain(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
@ -1148,6 +1176,7 @@ fn parse_index_chain(
lhs: Expr, lhs: Expr,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let idx_expr = parse_expr(input, state, lib, settings.level_up())?; let idx_expr = parse_expr(input, state, lib, settings.level_up())?;
@ -1321,21 +1350,25 @@ fn parse_index_chain(
} }
/// Parse an array literal. /// Parse an array literal.
#[cfg(not(feature = "no_index"))]
fn parse_array_literal( fn parse_array_literal(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut arr = StaticVec::new(); let mut arr = StaticVec::new();
while !input.peek().unwrap().0.is_eof() { 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( return Err(PERR::LiteralTooLarge(
"Size of array literal".to_string(), "Size of array literal".to_string(),
state.engine.max_array_size, state.engine.limits.max_array_size,
) )
.into_err(input.peek().unwrap().1)); .into_err(input.peek().unwrap().1));
} }
@ -1378,12 +1411,14 @@ fn parse_array_literal(
} }
/// Parse a map literal. /// Parse a map literal.
#[cfg(not(feature = "no_object"))]
fn parse_map_literal( fn parse_map_literal(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut map = StaticVec::new(); 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( return Err(PERR::LiteralTooLarge(
"Number of properties in object map literal".to_string(), "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)); .into_err(input.peek().unwrap().1));
} }
@ -1492,6 +1528,8 @@ fn parse_primary(
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos; settings.pos = *token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let (token, _) = match token { let (token, _) = match token {
@ -1626,6 +1664,8 @@ fn parse_unary(
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos; settings.pos = *token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
match token { match token {
@ -1708,7 +1748,9 @@ fn parse_unary(
Token::Pipe | Token::Or => { Token::Pipe | Token::Or => {
let mut state = ParseState::new( let mut state = ParseState::new(
state.engine, state.engine,
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_function_expr_depth,
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_function_expr_depth,
); );
@ -1809,6 +1851,8 @@ fn parse_op_assignment_stmt(
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos; settings.pos = *token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let op = match token { let op = match token {
@ -1835,6 +1879,7 @@ fn parse_op_assignment_stmt(
} }
/// Make a dot expression. /// Make a dot expression.
#[cfg(not(feature = "no_object"))]
fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> { fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
// idx_lhs[idx_expr].rhs // idx_lhs[idx_expr].rhs
@ -2048,6 +2093,8 @@ fn parse_binary_op(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.pos = lhs.position(); settings.pos = lhs.position();
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut root = lhs; let mut root = lhs;
@ -2081,6 +2128,8 @@ fn parse_binary_op(
settings = settings.level_up(); settings = settings.level_up();
settings.pos = pos; settings.pos = pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let cmp_def = Some(false); let cmp_def = Some(false);
@ -2164,6 +2213,8 @@ fn parse_expr(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.pos = input.peek().unwrap().1; settings.pos = input.peek().unwrap().1;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// Check if it is a custom syntax. // Check if it is a custom syntax.
@ -2291,6 +2342,8 @@ fn parse_if(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// if ... // if ...
settings.pos = eat_token(input, Token::If); settings.pos = eat_token(input, Token::If);
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// if guard { if_body } // if guard { if_body }
@ -2324,6 +2377,8 @@ fn parse_while(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// while ... // while ...
settings.pos = eat_token(input, Token::While); settings.pos = eat_token(input, Token::While);
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// while guard { body } // while guard { body }
@ -2346,6 +2401,8 @@ fn parse_loop(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// loop ... // loop ...
settings.pos = eat_token(input, Token::Loop); settings.pos = eat_token(input, Token::Loop);
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// loop { body } // loop { body }
@ -2364,6 +2421,8 @@ fn parse_for(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// for ... // for ...
settings.pos = eat_token(input, Token::For); settings.pos = eat_token(input, Token::For);
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// for name ... // for name ...
@ -2417,6 +2476,8 @@ fn parse_let(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// let/const... (specified in `var_type`) // let/const... (specified in `var_type`)
settings.pos = input.next().unwrap().1; settings.pos = input.next().unwrap().1;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// let name ... // let name ...
@ -2475,6 +2536,8 @@ fn parse_import(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// import ... // import ...
settings.pos = eat_token(input, Token::Import); settings.pos = eat_token(input, Token::Import);
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// import expr ... // import expr ...
@ -2509,12 +2572,14 @@ fn parse_import(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
fn parse_export( fn parse_export(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, _state: &mut ParseState,
_lib: &mut FunctionsLib, _lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
settings.pos = eat_token(input, Token::Export); 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(); 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)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut statements = StaticVec::new(); let mut statements = StaticVec::new();
@ -2657,6 +2723,8 @@ fn parse_expr_stmt(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
settings.pos = input.peek().unwrap().1; settings.pos = input.peek().unwrap().1;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
@ -2678,6 +2746,8 @@ fn parse_stmt(
x => x, x => x,
}; };
settings.pos = *token_pos; settings.pos = *token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
match token { match token {
@ -2703,7 +2773,9 @@ fn parse_stmt(
(Token::Fn, pos) => { (Token::Fn, pos) => {
let mut state = ParseState::new( let mut state = ParseState::new(
state.engine, state.engine,
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_function_expr_depth,
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_function_expr_depth,
); );
@ -2807,6 +2879,7 @@ fn parse_fn(
access: FnAccess, access: FnAccess,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<ScriptFnDef, ParseError> { ) -> Result<ScriptFnDef, ParseError> {
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let name = match input.next().unwrap() { let name = match input.next().unwrap() {
@ -2899,6 +2972,7 @@ fn parse_anon_fn(
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<(Expr, ScriptFnDef), ParseError> { ) -> Result<(Expr, ScriptFnDef), ParseError> {
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut params = Vec::new(); let mut params = Vec::new();
@ -2994,7 +3068,15 @@ impl Engine {
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {
let mut functions = 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,
);
let settings = ParseSettings { let settings = ParseSettings {
allow_if_expr: false, allow_if_expr: false,
allow_stmt_expr: false, allow_stmt_expr: false,
@ -3032,7 +3114,14 @@ impl Engine {
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> { ) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements: Vec<Stmt> = Default::default(); let mut statements: Vec<Stmt> = Default::default();
let mut functions = 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() { while !input.peek().unwrap().0.is_eof() {
let settings = ParseSettings { let settings = ParseSettings {

View File

@ -34,10 +34,10 @@ pub enum EvalAltResult {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
ErrorReadingScriptFile(PathBuf, Position, std::io::Error), 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), ErrorFunctionNotFound(String, Position),
/// An error has occurred inside a called function. /// 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), ErrorInFunctionCall(String, Box<EvalAltResult>, Position),
/// Access to `this` that is not bounded. /// Access to `this` that is not bounded.
ErrorUnboundedThis(Position), ErrorUnboundedThis(Position),

View File

@ -377,6 +377,7 @@ impl<'a> Scope<'a> {
} }
/// Update the access type of an entry in the Scope. /// 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 { 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"); let entry = self.0.get_mut(index).expect("invalid index in Scope");
entry.alias = Some(Box::new(alias)); entry.alias = Some(Box::new(alias));
@ -384,6 +385,7 @@ impl<'a> Scope<'a> {
} }
/// Get an iterator to entries in the Scope. /// Get an iterator to entries in the Scope.
#[cfg(not(feature = "no_module"))]
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> { pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
self.0.into_iter() self.0.into_iter()
} }

View File

@ -8,17 +8,20 @@ use crate::token::Position;
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use serde::de::{ use serde::de::{
DeserializeSeed, Deserializer, EnumAccess, Error, IntoDeserializer, MapAccess, SeqAccess, DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor,
VariantAccess, Visitor,
}; };
use serde::Deserialize; use serde::Deserialize;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::engine::Array; use crate::engine::Array;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::engine::Map; 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(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -46,8 +49,12 @@ impl<'de> DynamicDeserializer<'de> {
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> { 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( Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
type_name::<T>().into(), error.into(),
self.value.type_name().into(), self.value.type_name().into(),
Position::none(), 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"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .value
.downcast_ref::<f32>() .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")] #[cfg(feature = "no_float")]
return self.type_error_str("f32"); 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"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .value
.downcast_ref::<f64>() .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")] #[cfg(feature = "no_float")]
return self.type_error_str("f64"); return self.type_error_str("f64");
@ -359,11 +366,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
visitor.visit_newtype_struct(self) 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"))] #[cfg(not(feature = "no_index"))]
return self.value.downcast_ref::<Array>().map_or_else( return self.value.downcast_ref::<Array>().map_or_else(
|| self.type_error(), || self.type_error(),
|arr| visitor.visit_seq(IterateArray::new(arr.iter())), |arr| _visitor.visit_seq(IterateArray::new(arr.iter())),
); );
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
@ -387,11 +394,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
self.deserialize_seq(visitor) 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"))] #[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<Map>().map_or_else( return self.value.downcast_ref::<Map>().map_or_else(
|| self.type_error(), || 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")] #[cfg(feature = "no_object")]
@ -461,6 +468,7 @@ where
iter: ITER, iter: ITER,
} }
#[cfg(not(feature = "no_index"))]
impl<'a, ITER> IterateArray<'a, ITER> impl<'a, ITER> IterateArray<'a, ITER>
where where
ITER: Iterator<Item = &'a Dynamic>, ITER: Iterator<Item = &'a Dynamic>,
@ -502,6 +510,7 @@ where
values: VALUES, values: VALUES,
} }
#[cfg(not(feature = "no_object"))]
impl<'a, KEYS, VALUES> IterateMap<'a, KEYS, VALUES> impl<'a, KEYS, VALUES> IterateMap<'a, KEYS, VALUES>
where where
KEYS: Iterator<Item = &'a ImmutableString>, KEYS: Iterator<Item = &'a ImmutableString>,

View File

@ -10,27 +10,36 @@ use crate::engine::Array;
use crate::engine::Map; use crate::engine::Map;
use serde::ser::{ use serde::ser::{
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
SerializeTupleStruct, SerializeTupleVariant, Serializer, Serializer,
}; };
use serde::Serialize; 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. /// Serializer for `Dynamic` which is kept as a reference.
pub struct DynamicSerializer { pub struct DynamicSerializer {
/// Buffer to hold a temporary key. /// Buffer to hold a temporary key.
key: Dynamic, _key: Dynamic,
/// Buffer to hold a temporary value. /// Buffer to hold a temporary value.
value: Dynamic, _value: Dynamic,
} }
impl DynamicSerializer { impl DynamicSerializer {
/// Create a `DynamicSerializer` from a `Dynamic` value. /// Create a `DynamicSerializer` from a `Dynamic` value.
pub fn new(value: Dynamic) -> Self { pub fn new(_value: Dynamic) -> Self {
Self { Self {
key: Default::default(), _key: Default::default(),
value, _value,
} }
} }
} }
@ -280,13 +289,13 @@ impl Serializer for &mut DynamicSerializer {
self, self,
_name: &'static str, _name: &'static str,
_variant_index: u32, _variant_index: u32,
variant: &'static str, _variant: &'static str,
value: &T, _value: &T,
) -> Result<Self::Ok, Box<EvalAltResult>> { ) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let content = to_dynamic(value)?; let content = to_dynamic(_value)?;
make_variant(variant, content) make_variant(_variant, content)
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
@ -323,13 +332,13 @@ impl Serializer for &mut DynamicSerializer {
self, self,
_name: &'static str, _name: &'static str,
_variant_index: u32, _variant_index: u32,
variant: &'static str, _variant: &'static str,
len: usize, _len: usize,
) -> Result<Self::SerializeTupleVariant, Box<EvalAltResult>> { ) -> Result<Self::SerializeTupleVariant, Box<EvalAltResult>> {
#[cfg(not(any(feature = "no_object", feature = "no_index")))] #[cfg(not(any(feature = "no_object", feature = "no_index")))]
return Ok(TupleVariantSerializer { return Ok(TupleVariantSerializer {
variant, variant: _variant,
array: Array::with_capacity(len), array: Array::with_capacity(_len),
}); });
#[cfg(any(feature = "no_object", feature = "no_index"))] #[cfg(any(feature = "no_object", feature = "no_index"))]
{ {
@ -368,13 +377,13 @@ impl Serializer for &mut DynamicSerializer {
self, self,
_name: &'static str, _name: &'static str,
_variant_index: u32, _variant_index: u32,
variant: &'static str, _variant: &'static str,
len: usize, _len: usize,
) -> Result<Self::SerializeStructVariant, Box<EvalAltResult>> { ) -> Result<Self::SerializeStructVariant, Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(StructVariantSerializer { return Ok(StructVariantSerializer {
variant, variant: _variant,
map: Map::with_capacity(len), map: Map::with_capacity(_len),
}); });
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
@ -391,13 +400,13 @@ impl SerializeSeq for DynamicSerializer {
fn serialize_element<T: ?Sized + Serialize>( fn serialize_element<T: ?Sized + Serialize>(
&mut self, &mut self,
value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
let value = value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let arr = self.value.downcast_mut::<Array>().unwrap(); let arr = self._value.downcast_mut::<Array>().unwrap();
arr.push(value); arr.push(_value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
@ -407,7 +416,7 @@ impl SerializeSeq for DynamicSerializer {
// Close the sequence. // Close the sequence.
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self.value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
unreachable!() unreachable!()
} }
@ -419,13 +428,13 @@ impl SerializeTuple for DynamicSerializer {
fn serialize_element<T: ?Sized + Serialize>( fn serialize_element<T: ?Sized + Serialize>(
&mut self, &mut self,
value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
let value = value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let arr = self.value.downcast_mut::<Array>().unwrap(); let arr = self._value.downcast_mut::<Array>().unwrap();
arr.push(value); arr.push(_value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
@ -434,7 +443,7 @@ impl SerializeTuple for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self.value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
unreachable!() unreachable!()
} }
@ -446,13 +455,13 @@ impl SerializeTupleStruct for DynamicSerializer {
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(
&mut self, &mut self,
value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
let value = value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let arr = self.value.downcast_mut::<Array>().unwrap(); let arr = self._value.downcast_mut::<Array>().unwrap();
arr.push(value); arr.push(_value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
@ -461,7 +470,7 @@ impl SerializeTupleStruct for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self.value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
unreachable!() unreachable!()
} }
@ -471,10 +480,10 @@ impl SerializeMap for DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; 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"))] #[cfg(not(feature = "no_object"))]
{ {
self.key = key.serialize(&mut *self)?; self._key = _key.serialize(&mut *self)?;
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
@ -483,11 +492,11 @@ impl SerializeMap for DynamicSerializer {
fn serialize_value<T: ?Sized + Serialize>( fn serialize_value<T: ?Sized + Serialize>(
&mut self, &mut self,
value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let key = mem::take(&mut self.key) let key = mem::take(&mut self._key)
.take_immutable_string() .take_immutable_string()
.map_err(|typ| { .map_err(|typ| {
Box::new(EvalAltResult::ErrorMismatchOutputType( Box::new(EvalAltResult::ErrorMismatchOutputType(
@ -496,9 +505,9 @@ impl SerializeMap for DynamicSerializer {
Position::none(), Position::none(),
)) ))
})?; })?;
let value = value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self.value.downcast_mut::<Map>().unwrap(); let map = self._value.downcast_mut::<Map>().unwrap();
map.insert(key, value); map.insert(key, _value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
@ -507,22 +516,22 @@ impl SerializeMap for DynamicSerializer {
fn serialize_entry<K: ?Sized + Serialize, T: ?Sized + Serialize>( fn serialize_entry<K: ?Sized + Serialize, T: ?Sized + Serialize>(
&mut self, &mut self,
key: &K, _key: &K,
value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let key: Dynamic = key.serialize(&mut *self)?; let _key: Dynamic = _key.serialize(&mut *self)?;
let key = key.take_immutable_string().map_err(|typ| { let _key = _key.take_immutable_string().map_err(|typ| {
Box::new(EvalAltResult::ErrorMismatchOutputType( Box::new(EvalAltResult::ErrorMismatchOutputType(
"string".into(), "string".into(),
typ.into(), typ.into(),
Position::none(), Position::none(),
)) ))
})?; })?;
let value = value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self.value.downcast_mut::<Map>().unwrap(); let map = self._value.downcast_mut::<Map>().unwrap();
map.insert(key, value); map.insert(_key, _value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
@ -531,7 +540,7 @@ impl SerializeMap for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(self.value); return Ok(self._value);
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
unreachable!() unreachable!()
} }
@ -543,14 +552,14 @@ impl SerializeStruct for DynamicSerializer {
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(
&mut self, &mut self,
key: &'static str, _key: &'static str,
value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let value = value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self.value.downcast_mut::<Map>().unwrap(); let map = self._value.downcast_mut::<Map>().unwrap();
map.insert(key.into(), value); map.insert(_key.into(), _value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
@ -559,7 +568,7 @@ impl SerializeStruct for DynamicSerializer {
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(self.value); return Ok(self._value);
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
unreachable!() unreachable!()
} }

View File

@ -6,7 +6,7 @@ use crate::utils::ImmutableString;
use serde::de::{Deserializer, Visitor}; use serde::de::{Deserializer, Visitor};
use crate::stdlib::any::type_name; use crate::stdlib::{any::type_name, boxed::Box};
/// Deserializer for `ImmutableString`. /// Deserializer for `ImmutableString`.
pub struct ImmutableStringDeserializer<'a> { pub struct ImmutableStringDeserializer<'a> {

View File

@ -1,12 +1,17 @@
//! Configuration settings for `Engine`. //! Configuration settings for `Engine`.
use crate::engine::Engine; use crate::engine::Engine;
use crate::module::ModuleResolver;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::PackageLibrary; use crate::packages::PackageLibrary;
use crate::token::{is_valid_identifier, Token}; 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 { impl Engine {
/// Load a new package into the `Engine`. /// Load a new package into the `Engine`.
@ -41,21 +46,21 @@ impl Engine {
/// infinite recursion and stack overflows. /// infinite recursion and stack overflows.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self { 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 self
} }
/// The maximum levels of function calls allowed for a script. /// The maximum levels of function calls allowed for a script.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn max_call_levels(&self) -> usize { 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 /// Set the maximum number of operations allowed for a script to run to avoid
/// consuming too much resources (0 for unlimited). /// consuming too much resources (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self { 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 0
} else { } else {
operations operations
@ -66,20 +71,20 @@ impl Engine {
/// The maximum number of operations allowed for a script to run (0 for unlimited). /// The maximum number of operations allowed for a script to run (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn max_operations(&self) -> u64 { pub fn max_operations(&self) -> u64 {
self.max_operations self.limits.max_operations
} }
/// Set the maximum number of imported modules allowed for a script. /// Set the maximum number of imported modules allowed for a script.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self { pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
self.max_modules = modules; self.limits.max_modules = modules;
self self
} }
/// The maximum number of imported modules allowed for a script. /// The maximum number of imported modules allowed for a script.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn max_modules(&self) -> usize { pub fn max_modules(&self) -> usize {
self.max_modules self.limits.max_modules
} }
/// Set the depth limits for expressions (0 for unlimited). /// Set the depth limits for expressions (0 for unlimited).
@ -89,12 +94,12 @@ impl Engine {
max_expr_depth: usize, max_expr_depth: usize,
max_function_expr_depth: usize, max_function_expr_depth: usize,
) -> &mut Self { ) -> &mut Self {
self.max_expr_depth = if max_expr_depth == usize::MAX { self.limits.max_expr_depth = if max_expr_depth == usize::MAX {
0 0
} else { } else {
max_expr_depth 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 0
} else { } else {
max_function_expr_depth max_function_expr_depth
@ -105,33 +110,33 @@ impl Engine {
/// The depth limit for expressions (0 for unlimited). /// The depth limit for expressions (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn max_expr_depth(&self) -> usize { 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). /// The depth limit for expressions in functions (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn max_function_expr_depth(&self) -> usize { 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). /// Set the maximum length of strings (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self { 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 self
} }
/// The maximum length of strings (0 for unlimited). /// The maximum length of strings (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn max_string_size(&self) -> usize { 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). /// Set the maximum length of arrays (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self { 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 self
} }
@ -139,14 +144,14 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub fn max_array_size(&self) -> usize { 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). /// Set the maximum length of object maps (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self { 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 self
} }
@ -154,7 +159,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn max_map_size(&self) -> usize { 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`. /// Set the module resolution service used by the `Engine`.

View File

@ -14,11 +14,14 @@ use crate::utils::StaticVec;
use crate::stdlib::{ use crate::stdlib::{
boxed::Box, boxed::Box,
fmt, format, fmt, format,
rc::Rc,
string::{String, ToString}, 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. /// A general expression evaluation trait object.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type FnCustomSyntaxEval = dyn Fn( 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 { TokenIterator {
engine, engine,
state: TokenizeState { 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, non_unary: false,
comment_level: 0, comment_level: 0,
end_with_none: false, end_with_none: false,

View File

@ -166,9 +166,9 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
module.set_raw_fn( module.set_raw_fn(
"call_with_arg", "call_with_arg",
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()], &[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>(); 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 *engine
.consume(r#"import "testing" as ttt; ttt::hidden()"#) .consume(r#"import "testing" as ttt; ttt::hidden()"#)
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "ttt::hidden" EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "ttt::hidden ()"
)); ));
Ok(()) Ok(())