Merge pull request #211 from schungx/master

Fix features.
This commit is contained in:
Stephen Chung 2020-08-06 22:52:06 +08:00 committed by GitHub
commit d0fc90d577
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 364 additions and 327 deletions

View File

@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- master - master
- closures
pull_request: {} pull_request: {}
jobs: jobs:
@ -23,7 +22,7 @@ jobs:
- "--features sync" - "--features sync"
- "--features no_optimize" - "--features no_optimize"
- "--features no_float" - "--features no_float"
- "--features only_i32" - "--tests --features only_i32"
- "--features only_i64" - "--features only_i64"
- "--features no_index" - "--features no_index"
- "--features no_object" - "--features no_object"
@ -51,7 +50,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all {{matrix.flags}} args: --all ${{matrix.flags}}
# no-std builds are a bit more extensive to test # no-std builds are a bit more extensive to test
no_std_build: no_std_build:
name: NoStdBuild name: NoStdBuild

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rhai" name = "rhai"
version = "0.18.0" version = "0.19.0"
edition = "2018" edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
description = "Embedded scripting for Rust" description = "Embedded scripting for Rust"
@ -23,7 +23,6 @@ smallvec = { version = "1.4.1", default-features = false }
[features] [features]
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"] #default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
default = [] default = []
plugins = [] # custom plugins support
unchecked = [] # unchecked arithmetic unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync sync = [] # restrict to only types that implement Send + Sync
no_optimize = [] # no script optimizer no_optimize = [] # no script optimizer
@ -32,7 +31,7 @@ only_i32 = [] # set INT=i32 (useful for 32-bit systems)
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
no_index = [] # no arrays and indexing no_index = [] # no arrays and indexing
no_object = [] # no custom objects no_object = [] # no custom objects
no_function = [] # no script-defined functions no_function = [ "no_closure" ] # no script-defined functions (meaning no closures)
no_closure = [] # no automatic sharing and capture of anonymous functions to external variables no_closure = [] # no automatic sharing and capture of anonymous functions to external variables
no_module = [] # no modules no_module = [] # no modules
internals = [] # expose internal data structures internals = [] # expose internal data structures

View File

@ -1,14 +1,18 @@
Rhai Release Notes Rhai Release Notes
================== ==================
Version 0.18.0 Version 0.19.0
==============
Version 0.18.1
============== ==============
This version adds: This version adds:
* Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions. * Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions.
* Currying of function pointers. * Currying of function pointers.
* Closures - auto-currying of anonymous functions to capture shared variables from the external scope. * Closures - auto-currying of anonymous functions to capture shared variables from the external scope. Use the `no_closure` feature to disable sharing values and capturing.
* Binding the `this` pointer in a function pointer `call`. * Binding the `this` pointer in a function pointer `call`.
* Capturing call scope via `func!(...)` syntax. * Capturing call scope via `func!(...)` syntax.

View File

@ -1,5 +1,5 @@
{ {
"version": "0.18.0", "version": "0.19.0",
"repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoHome": "https://github.com/jonathandturner/rhai/blob/master",
"repoTree": "https://github.com/jonathandturner/rhai/tree/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master",
"rootUrl": "", "rootUrl": "",

View File

@ -200,8 +200,10 @@ fn implementation_func(
// Evaluate the condition expression // Evaluate the condition expression
let stop = !engine.eval_expression_tree(context, scope, condition)? let stop = !engine.eval_expression_tree(context, scope, condition)?
.as_bool().map_err(|_| EvalAltResult::ErrorBooleanArgMismatch( .as_bool()
"do-while".into(), expr.position()))?; .map_err(|_| EvalAltResult::ErrorBooleanArgMismatch(
"do-while".into(), expr.position()
))?;
if stop { if stop {
break; break;

View File

@ -36,6 +36,8 @@ update(array[0]); // <- 'array[0]' is an expression returning a calculated val
array[0].update(); // <- call in method-call style will update 'a' array[0].update(); // <- call in method-call style will update 'a'
``` ```
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Number of Parameters Number of Parameters
-------------------- --------------------
@ -51,8 +53,8 @@ The following table illustrates the differences:
| Rhai script | _n_ | `this` | `fn method(x, y) {}` | | Rhai script | _n_ | `this` | `fn method(x, y) {}` |
`&mut` is Efficient (Except for `ImmutableString`) `&mut` is Efficient, Except for `ImmutableString`
------------------------------------------------ -----------------------------------------------
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.

View File

@ -7,10 +7,17 @@ Import a Module
`import` Statement `import` Statement
----------------- -----------------
A module can be _imported_ via the `import` statement, and its members are accessed via '`::`' similar to C++. A module can be _imported_ via the `import` statement, and be given a name.
Its members can be accessed via '`::`' similar to C++.
A module that is only `import`-ed but not under any module name is commonly used for initialization purposes,
where the module script contains initialization statements that puts the functions registered with the
[`Engine`] into a particular state.
```rust ```rust
import "crypto" as lock; // import the script file 'crypto.rhai' as a module named 'lock' import "crypto_init"; // run the script file 'crypto_init.rhai' without creating an imported module
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
lock::encrypt(secret); // use functions defined under the module via '::' lock::encrypt(secret); // use functions defined under the module via '::'

View File

@ -83,6 +83,8 @@ engine
***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter ***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter
so that invoking methods can update the types. All other parameters in Rhai are passed by value (i.e. clones).* so that invoking methods can update the types. All other parameters in Rhai are passed by value (i.e. clones).*
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Use the Custom Type in Scripts Use the Custom Type in Scripts
----------------------------- -----------------------------
@ -125,8 +127,8 @@ Under [`no_object`], however, the _method_ style of function calls
let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?; let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?;
``` ```
[`type_of()`] `type_of()` a Custom Type
------------- -------------------------
[`type_of()`] works fine with custom types and returns the name of the type. [`type_of()`] works fine with custom types and returns the name of the type.

View File

@ -42,3 +42,5 @@ let result = engine.eval::<String>(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
``` ```
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**

View File

@ -38,7 +38,7 @@ impl ModuleResolver for MyModuleResolver {
let module: Module = load_secret_module(path); let module: Module = load_secret_module(path);
Ok(module) Ok(module)
} else { } else {
Err(Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos))) Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
} }
} }
} }

View File

@ -95,10 +95,11 @@ When there is a mutable reference to the `this` object (i.e. the first argument)
there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain. there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain.
Example - Passing a Function Pointer to a Rust Function Example - Passing a Callback to a Rust Function
------------------------------------------------------ ----------------------------------------------
The low-level API is useful when there is a need to interact with the scripting [`Engine`] within a function. The low-level API is useful when there is a need to interact with the scripting [`Engine`]
within a function.
The following example registers a function that takes a [function pointer] as an argument, The following example registers a function that takes a [function pointer] as an argument,
then calls it within the same [`Engine`]. This way, a _callback_ function can be provided then calls it within the same [`Engine`]. This way, a _callback_ function can be provided
@ -140,6 +141,24 @@ let result = engine.eval::<i64>(r#"
``` ```
TL;DR - Why `read_lock` and `write_lock`
---------------------------------------
The `Dynamic` API that casts it to a reference to a particular data type is `read_lock`
(for an immutable reference) and `write_lock` (for a mutable reference).
As the naming shows, something is _locked_ in order to allow this access, and that something
is a _shared value_ created by [capturing][automatic currying] variables from [closures].
Shared values are implemented as `Rc<RefCell<Dynamic>>` (`Arc<RwLock<Dynamic>>` under [`sync`]).
If the value is _not_ a shared value, or if running under [`no_closure`] where there is
no [capturing][automatic currying], this API de-sugars to a simple `downcast_ref` and `downcast_mut`.
If the value is a shared value, then it is first locked and the returned lock guard
then allows access to the underlying value in the specified type.
Hold Multiple References Hold Multiple References
------------------------ ------------------------
@ -152,9 +171,9 @@ to partition the slice:
let (first, rest) = args.split_at_mut(1); let (first, rest) = args.split_at_mut(1);
// Mutable reference to the first parameter // Mutable reference to the first parameter
let this_ptr = first[0].downcast_mut::<A>().unwrap(); let this_ptr: &mut Dynamic = &mut *first[0].write_lock::<A>().unwrap();
// Immutable reference to the second value parameter // Immutable reference to the second value parameter
// This can be mutable but there is no point because the parameter is passed by value // This can be mutable but there is no point because the parameter is passed by value
let value_ref = rest[0].read_lock::<B>().unwrap(); let value_ref: &Dynamic = &*rest[0].read_lock::<B>().unwrap();
``` ```

View File

@ -410,7 +410,7 @@ impl fmt::Display for Dynamic {
} }
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
Union::Shared(cell) => fmt::Display::fmt(*cell.read_lock().unwrap(), f), Union::Shared(cell) => fmt::Display::fmt(&*cell.read().unwrap(), f),
} }
} }
} }
@ -448,7 +448,7 @@ impl fmt::Debug for Dynamic {
} }
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
Union::Shared(cell) => fmt::Display::fmt(*cell.read_lock().unwrap(), f), Union::Shared(cell) => fmt::Debug::fmt(&*cell.read().unwrap(), f),
} }
} }
} }

View File

@ -3,10 +3,9 @@
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::engine::{Engine, Imports, State}; use crate::engine::{Engine, Imports, State};
use crate::error::ParseError; use crate::error::ParseError;
use crate::fn_call::ensure_no_data_race;
use crate::fn_native::{IteratorFn, SendSync}; use crate::fn_native::{IteratorFn, SendSync};
use crate::module::{FuncReturn, Module}; use crate::module::{FuncReturn, Module};
use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::optimize::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;
@ -24,14 +23,22 @@ use crate::{
}; };
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::{engine::get_script_function_by_signature, fn_args::FuncArgs, utils::StaticVec}; use crate::{
engine::get_script_function_by_signature, fn_args::FuncArgs, fn_call::ensure_no_data_race,
utils::StaticVec,
};
#[cfg(not(feature = "no_optimize"))]
use crate::optimize::optimize_into_ast;
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
boxed::Box, boxed::Box,
mem,
}; };
#[cfg(not(feature = "no_optimize"))]
use crate::stdlib::mem;
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf}; use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
@ -601,21 +608,13 @@ impl Engine {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> { fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
let mut f = File::open(path.clone()).map_err(|err| { let mut f = File::open(path.clone()).map_err(|err| {
Box::new(EvalAltResult::ErrorReadingScriptFile( EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err)
path.clone(),
Position::none(),
err,
))
})?; })?;
let mut contents = String::new(); let mut contents = String::new();
f.read_to_string(&mut contents).map_err(|err| { f.read_to_string(&mut contents).map_err(|err| {
Box::new(EvalAltResult::ErrorReadingScriptFile( EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err)
path.clone(),
Position::none(),
err,
))
})?; })?;
Ok(contents) Ok(contents)
@ -1035,11 +1034,12 @@ impl Engine {
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
return result.try_cast::<T>().ok_or_else(|| { return result.try_cast::<T>().ok_or_else(|| {
Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::none(), Position::none(),
)) )
.into()
}); });
} }
@ -1183,11 +1183,12 @@ impl Engine {
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
return result.try_cast().ok_or_else(|| { return result.try_cast().ok_or_else(|| {
Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::none(), Position::none(),
)) )
.into()
}); });
} }
@ -1271,13 +1272,8 @@ impl Engine {
) -> FuncReturn<Dynamic> { ) -> FuncReturn<Dynamic> {
let lib = lib.as_ref(); let lib = lib.as_ref();
let mut args: StaticVec<_> = arg_values.iter_mut().collect(); let mut args: StaticVec<_> = arg_values.iter_mut().collect();
let fn_def = let fn_def = get_script_function_by_signature(lib, name, args.len(), true)
get_script_function_by_signature(lib, name, args.len(), true).ok_or_else(|| { .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;
Box::new(EvalAltResult::ErrorFunctionNotFound(
name.into(),
Position::none(),
))
})?;
let mut state = State::new(); let mut state = State::new();
let mut mods = Imports::new(); let mut mods = Imports::new();

View File

@ -228,9 +228,7 @@ impl Target<'_> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Self::LockGuard((r, _)) => **r = new_val, Self::LockGuard((r, _)) => **r = new_val,
Self::Value(_) => { Self::Value(_) => {
return Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS( return EvalAltResult::ErrorAssignmentToUnknownLHS(Position::none()).into();
Position::none(),
)))
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar(string, index, _) if string.is::<ImmutableString>() => { Self::StringChar(string, index, _) if string.is::<ImmutableString>() => {
@ -517,12 +515,7 @@ pub fn search_imports<'s>(
.rev() .rev()
.find(|(n, _)| n == root) .find(|(n, _)| n == root)
.map(|(_, m)| m) .map(|(_, m)| m)
.ok_or_else(|| { .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *root_pos))?
Box::new(EvalAltResult::ErrorModuleNotFound(
root.to_string(),
*root_pos,
))
})?
}) })
} }
@ -550,12 +543,7 @@ pub fn search_imports_mut<'s>(
.rev() .rev()
.find(|(n, _)| n == root) .find(|(n, _)| n == root)
.map(|(_, m)| m) .map(|(_, m)| m)
.ok_or_else(|| { .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *root_pos))?
Box::new(EvalAltResult::ErrorModuleNotFound(
root.to_string(),
*root_pos,
))
})?
}) })
} }
@ -577,10 +565,11 @@ pub fn search_namespace<'s, 'a>(
.get_qualified_var_mut(*hash_var) .get_qualified_var_mut(*hash_var)
.map_err(|err| match *err { .map_err(|err| match *err {
EvalAltResult::ErrorVariableNotFound(_, _) => { EvalAltResult::ErrorVariableNotFound(_, _) => {
Box::new(EvalAltResult::ErrorVariableNotFound( EvalAltResult::ErrorVariableNotFound(
format!("{}{}", modules, name), format!("{}{}", modules, name),
*pos, *pos,
)) )
.into()
} }
_ => err.new_position(*pos), _ => err.new_position(*pos),
})?; })?;
@ -612,7 +601,7 @@ pub fn search_scope_only<'s, 'a>(
if let Some(val) = this_ptr { if let Some(val) = this_ptr {
return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos)); return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos));
} else { } else {
return Err(Box::new(EvalAltResult::ErrorUnboundThis(*pos))); return EvalAltResult::ErrorUnboundThis(*pos).into();
} }
} }
@ -625,7 +614,7 @@ pub fn search_scope_only<'s, 'a>(
// Find the variable in the scope // Find the variable in the scope
scope scope
.get_index(name) .get_index(name)
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), *pos)))? .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.into(), *pos))?
.0 .0
}; };
@ -634,7 +623,7 @@ pub fn search_scope_only<'s, 'a>(
// Check for data race - probably not necessary because the only place it should conflict is in a method call // Check for data race - probably not necessary because the only place it should conflict is in a method call
// when the object variable is also used as a parameter. // when the object variable is also used as a parameter.
// if cfg!(not(feature = "no_closure")) && val.is_locked() { // if cfg!(not(feature = "no_closure")) && val.is_locked() {
// return Err(Box::new(EvalAltResult::ErrorDataRace(name.into(), *pos))); // return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
// } // }
Ok((val, name, typ, *pos)) Ok((val, name, typ, *pos))
@ -966,10 +955,7 @@ impl Engine {
} }
} }
// Syntax error // Syntax error
_ => Err(Box::new(EvalAltResult::ErrorDotExpr( _ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(),
"".into(),
rhs.position(),
))),
} }
} }
@ -1016,10 +1002,8 @@ impl Engine {
// Constants cannot be modified // Constants cannot be modified
match typ { match typ {
ScopeEntryType::Constant if new_val.is_some() => { ScopeEntryType::Constant if new_val.is_some() => {
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
var_name.to_string(), .into();
pos,
)));
} }
ScopeEntryType::Constant | ScopeEntryType::Normal => (), ScopeEntryType::Constant | ScopeEntryType::Normal => (),
} }
@ -1033,9 +1017,7 @@ impl Engine {
} }
// {expr}.??? = ??? or {expr}[???] = ??? // {expr}.??? = ??? or {expr}[???] = ???
expr if new_val.is_some() => { expr if new_val.is_some() => {
return Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS( return EvalAltResult::ErrorAssignmentToUnknownLHS(expr.position()).into();
expr.position(),
)));
} }
// {expr}.??? or {expr}[???] // {expr}.??? or {expr}[???]
expr => { expr => {
@ -1160,12 +1142,10 @@ impl Engine {
arr.get_mut(index as usize) arr.get_mut(index as usize)
.map(Target::from) .map(Target::from)
.ok_or_else(|| { .ok_or_else(|| {
Box::new(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)) EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into()
}) })
} else { } else {
Err(Box::new(EvalAltResult::ErrorArrayBounds( EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into()
arr_len, index, idx_pos,
)))
} }
} }
@ -1200,13 +1180,11 @@ impl Engine {
if index >= 0 { if index >= 0 {
let offset = index as usize; let offset = index as usize;
let ch = s.chars().nth(offset).ok_or_else(|| { let ch = s.chars().nth(offset).ok_or_else(|| {
Box::new(EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)) EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)
})?; })?;
Ok(Target::StringChar(val, offset, ch.into())) Ok(Target::StringChar(val, offset, ch.into()))
} else { } else {
Err(Box::new(EvalAltResult::ErrorStringBounds( EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos).into()
chars_len, index, idx_pos,
)))
} }
} }
@ -1227,10 +1205,11 @@ impl Engine {
}) })
} }
_ => Err(Box::new(EvalAltResult::ErrorIndexingType( _ => EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).into(), self.map_type_name(val.type_name()).into(),
Position::none(), Position::none(),
))), )
.into(),
} }
} }
@ -1284,15 +1263,15 @@ impl Engine {
// Only allows String or char // Only allows String or char
Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(&s).into()), Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(&s).into()),
Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()),
_ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))), _ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
}, },
Dynamic(Union::Str(rhs_value)) => match lhs_value { Dynamic(Union::Str(rhs_value)) => match lhs_value {
// Only allows String or char // Only allows String or char
Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()), Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()),
Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()),
_ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))), _ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
}, },
_ => Err(Box::new(EvalAltResult::ErrorInExpr(rhs.position()))), _ => EvalAltResult::ErrorInExpr(rhs.position()).into(),
} }
} }
@ -1323,7 +1302,7 @@ impl Engine {
if let Some(val) = this_ptr { if let Some(val) = this_ptr {
Ok(val.clone()) Ok(val.clone())
} else { } else {
Err(Box::new(EvalAltResult::ErrorUnboundThis((x.0).1))) EvalAltResult::ErrorUnboundThis((x.0).1).into()
} }
} }
Expr::Variable(_) => { Expr::Variable(_) => {
@ -1454,16 +1433,13 @@ impl Engine {
Ok(Default::default()) Ok(Default::default())
} }
// Error assignment to constant // Error assignment to constant
expr if expr.is_constant() => { expr if expr.is_constant() => EvalAltResult::ErrorAssignmentToConstant(
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( expr.get_constant_str(),
expr.get_constant_str(),
expr.position(),
)))
}
// Syntax error
expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS(
expr.position(), expr.position(),
))), )
.into(),
// Syntax error
expr => EvalAltResult::ErrorAssignmentToUnknownLHS(expr.position()).into(),
} }
} }
@ -1632,7 +1608,7 @@ impl Engine {
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(expr.position()))) .map_err(|_| EvalAltResult::ErrorLogicGuard(expr.position()).into())
.and_then(|guard_val| { .and_then(|guard_val| {
if guard_val { if guard_val {
self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level)
@ -1665,9 +1641,7 @@ impl Engine {
} }
} }
Ok(false) => return Ok(Default::default()), Ok(false) => return Ok(Default::default()),
Err(_) => { Err(_) => return EvalAltResult::ErrorLogicGuard(expr.position()).into(),
return Err(Box::new(EvalAltResult::ErrorLogicGuard(expr.position())))
}
} }
}, },
@ -1727,43 +1701,45 @@ impl Engine {
state.scope_level -= 1; state.scope_level -= 1;
Ok(Default::default()) Ok(Default::default())
} else { } else {
Err(Box::new(EvalAltResult::ErrorFor(x.1.position()))) EvalAltResult::ErrorFor(x.1.position()).into()
} }
} }
// Continue statement // Continue statement
Stmt::Continue(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(false, *pos))), Stmt::Continue(pos) => EvalAltResult::ErrorLoopBreak(false, *pos).into(),
// Break statement // Break statement
Stmt::Break(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(true, *pos))), Stmt::Break(pos) => EvalAltResult::ErrorLoopBreak(true, *pos).into(),
// Return value // Return value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
let expr = x.1.as_ref().unwrap(); let expr = x.1.as_ref().unwrap();
Err(Box::new(EvalAltResult::Return( EvalAltResult::Return(
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
(x.0).1, (x.0).1,
))) )
.into()
} }
// Empty return // Empty return
Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Return => {
Err(Box::new(EvalAltResult::Return(Default::default(), (x.0).1))) EvalAltResult::Return(Default::default(), (x.0).1).into()
} }
// Throw value // Throw value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => { Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
let expr = x.1.as_ref().unwrap(); let expr = x.1.as_ref().unwrap();
let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
Err(Box::new(EvalAltResult::ErrorRuntime( EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".into()), val.take_string().unwrap_or_else(|_| "".into()),
(x.0).1, (x.0).1,
))) )
.into()
} }
// Empty throw // Empty throw
Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Exception => { Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Exception => {
Err(Box::new(EvalAltResult::ErrorRuntime("".into(), (x.0).1))) EvalAltResult::ErrorRuntime("".into(), (x.0).1).into()
} }
Stmt::ReturnWithVal(_) => unreachable!(), Stmt::ReturnWithVal(_) => unreachable!(),
@ -1806,12 +1782,12 @@ impl Engine {
// Import statement // Import statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => { Stmt::Import(x) => {
let (expr, (name, _pos), _) = x.as_ref(); let (expr, alias, _pos) = x.as_ref();
// Guard against too many modules // Guard against too many modules
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if state.modules >= self.limits.max_modules { if state.modules >= self.limits.max_modules {
return Err(Box::new(EvalAltResult::ErrorTooManyModules(*_pos))); return EvalAltResult::ErrorTooManyModules(*_pos).into();
} }
if let Some(path) = self if let Some(path) = self
@ -1820,20 +1796,23 @@ impl Engine {
{ {
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();
mods.push((name.clone().into(), module)); if let Some((name, _)) = alias {
module.index_all_sub_modules();
mods.push((name.clone().into(), module));
}
state.modules += 1; state.modules += 1;
Ok(Default::default()) Ok(Default::default())
} else { } else {
Err(Box::new(EvalAltResult::ErrorModuleNotFound( Err(
path.to_string(), EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
expr.position(), .into(),
))) )
} }
} else { } else {
Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) EvalAltResult::ErrorImportExpr(expr.position()).into()
} }
} }
@ -1846,10 +1825,7 @@ impl Engine {
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id); let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);
scope.set_entry_alias(index, alias.clone()); scope.set_entry_alias(index, alias.clone());
} else { } else {
return Err(Box::new(EvalAltResult::ErrorVariableNotFound( return EvalAltResult::ErrorVariableNotFound(id.into(), *id_pos).into();
id.into(),
*id_pos,
)));
} }
} }
Ok(Default::default()) Ok(Default::default())
@ -1973,26 +1949,29 @@ impl Engine {
let (arr, map, s) = calc_size(result.as_ref().unwrap()); let (arr, map, s) = calc_size(result.as_ref().unwrap());
if s > self.limits.max_string_size { if s > self.limits.max_string_size {
Err(Box::new(EvalAltResult::ErrorDataTooLarge( EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
self.limits.max_string_size, self.limits.max_string_size,
s, s,
Position::none(), Position::none(),
))) )
.into()
} else if arr > self.limits.max_array_size { } else if arr > self.limits.max_array_size {
Err(Box::new(EvalAltResult::ErrorDataTooLarge( EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(), "Size of array".to_string(),
self.limits.max_array_size, self.limits.max_array_size,
arr, arr,
Position::none(), Position::none(),
))) )
.into()
} else if map > self.limits.max_map_size { } else if map > self.limits.max_map_size {
Err(Box::new(EvalAltResult::ErrorDataTooLarge( EvalAltResult::ErrorDataTooLarge(
"Number of properties in object map".to_string(), "Number of properties in object map".to_string(),
self.limits.max_map_size, self.limits.max_map_size,
map, map,
Position::none(), Position::none(),
))) )
.into()
} else { } else {
result result
} }
@ -2006,16 +1985,14 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
// Guard against too many operations // Guard against too many operations
if self.limits.max_operations > 0 && state.operations > self.limits.max_operations { if self.limits.max_operations > 0 && state.operations > self.limits.max_operations {
return Err(Box::new(EvalAltResult::ErrorTooManyOperations( return EvalAltResult::ErrorTooManyOperations(Position::none()).into();
Position::none(),
)));
} }
// Report progress - only in steps // Report progress - only in steps
if let Some(progress) = &self.progress { if let Some(progress) = &self.progress {
if !progress(&state.operations) { if !progress(&state.operations) {
// Terminate script if progress returns false // Terminate script if progress returns false
return Err(Box::new(EvalAltResult::ErrorTerminated(Position::none()))); return EvalAltResult::ErrorTerminated(Position::none()).into();
} }
} }

View File

@ -34,6 +34,7 @@ use crate::engine::{FN_IDX_GET, FN_IDX_SET};
use crate::engine::{Map, Target, FN_GET, FN_SET}; use crate::engine::{Map, Target, FN_GET, FN_SET};
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_function"))]
use crate::scope::Entry as ScopeEntry; use crate::scope::Entry as ScopeEntry;
use crate::stdlib::{ use crate::stdlib::{
@ -48,6 +49,7 @@ use crate::stdlib::{
}; };
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_function"))]
use crate::stdlib::{collections::HashSet, string::String}; use crate::stdlib::{collections::HashSet, string::String};
/// Extract the property name from a getter function name. /// Extract the property name from a getter function name.
@ -140,6 +142,7 @@ impl Drop for ArgBackup<'_> {
// Add captured variables into scope // Add captured variables into scope
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_function"))]
fn add_captured_variables_into_scope<'s>( fn add_captured_variables_into_scope<'s>(
externals: &HashSet<String>, externals: &HashSet<String>,
captured: Scope<'s>, captured: Scope<'s>,
@ -175,10 +178,11 @@ pub fn ensure_no_data_race(
.enumerate() .enumerate()
.find(|(_, a)| a.is_locked()) .find(|(_, a)| a.is_locked())
{ {
return Err(Box::new(EvalAltResult::ErrorDataRace( return EvalAltResult::ErrorDataRace(
format!("argument #{} of function '{}'", n + 1 + skip, fn_name), format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
Position::none(), Position::none(),
))); )
.into();
} }
} }
@ -234,22 +238,22 @@ impl Engine {
return Ok(match fn_name { return Ok(match fn_name {
KEYWORD_PRINT => ( KEYWORD_PRINT => (
(self.print)(result.as_str().map_err(|typ| { (self.print)(result.as_str().map_err(|typ| {
Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(), self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(), typ.into(),
Position::none(), Position::none(),
)) )
})?) })?)
.into(), .into(),
false, false,
), ),
KEYWORD_DEBUG => ( KEYWORD_DEBUG => (
(self.debug)(result.as_str().map_err(|typ| { (self.debug)(result.as_str().map_err(|typ| {
Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(), self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(), typ.into(),
Position::none(), Position::none(),
)) )
})?) })?)
.into(), .into(),
false, false,
@ -273,56 +277,60 @@ impl Engine {
// Getter function not found? // Getter function not found?
if let Some(prop) = extract_prop_from_getter(fn_name) { if let Some(prop) = extract_prop_from_getter(fn_name) {
return Err(Box::new(EvalAltResult::ErrorDotExpr( return EvalAltResult::ErrorDotExpr(
format!( format!(
"Unknown property '{}' for {}, or it is write-only", "Unknown property '{}' for {}, or it is write-only",
prop, prop,
self.map_type_name(args[0].type_name()) self.map_type_name(args[0].type_name())
), ),
Position::none(), Position::none(),
))); )
.into();
} }
// Setter function not found? // Setter function not found?
if let Some(prop) = extract_prop_from_setter(fn_name) { if let Some(prop) = extract_prop_from_setter(fn_name) {
return Err(Box::new(EvalAltResult::ErrorDotExpr( return EvalAltResult::ErrorDotExpr(
format!( format!(
"Unknown property '{}' for {}, or it is read-only", "Unknown property '{}' for {}, or it is read-only",
prop, prop,
self.map_type_name(args[0].type_name()) self.map_type_name(args[0].type_name())
), ),
Position::none(), Position::none(),
))); )
.into();
} }
// index getter function not found? // index getter function not found?
#[cfg(not(feature = "no_index"))] #[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 EvalAltResult::ErrorFunctionNotFound(
format!( format!(
"{} [{}]", "{} [{}]",
self.map_type_name(args[0].type_name()), self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()), self.map_type_name(args[1].type_name()),
), ),
Position::none(), Position::none(),
))); )
.into();
} }
// index setter function not found? // index setter function not found?
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if fn_name == FN_IDX_SET { if fn_name == FN_IDX_SET {
return Err(Box::new(EvalAltResult::ErrorFunctionNotFound( return EvalAltResult::ErrorFunctionNotFound(
format!( format!(
"{} [{}]=", "{} [{}]=",
self.map_type_name(args[0].type_name()), self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()), self.map_type_name(args[1].type_name()),
), ),
Position::none(), Position::none(),
))); )
.into();
} }
// Raise error // Raise error
Err(Box::new(EvalAltResult::ErrorFunctionNotFound( EvalAltResult::ErrorFunctionNotFound(
format!( format!(
"{} ({})", "{} ({})",
fn_name, fn_name,
@ -336,7 +344,8 @@ impl Engine {
.join(", ") .join(", ")
), ),
Position::none(), Position::none(),
))) )
.into()
} }
/// Call a script-defined function. /// Call a script-defined function.
@ -397,17 +406,15 @@ impl Engine {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::Return(x, _) => Ok(x),
EvalAltResult::ErrorInFunctionCall(name, err, _) => { EvalAltResult::ErrorInFunctionCall(name, err, _) => {
Err(Box::new(EvalAltResult::ErrorInFunctionCall( EvalAltResult::ErrorInFunctionCall(
format!("{} > {}", fn_name, name), format!("{} > {}", fn_name, name),
err, err,
Position::none(), Position::none(),
))) )
.into()
} }
_ => Err(Box::new(EvalAltResult::ErrorInFunctionCall( _ => EvalAltResult::ErrorInFunctionCall(fn_name.to_string(), err, Position::none())
fn_name.to_string(), .into(),
err,
Position::none(),
))),
}); });
// Remove all local variables // Remove all local variables
@ -449,11 +456,11 @@ impl Engine {
hash_script: u64, hash_script: u64,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
is_method: bool, _is_method: bool,
pub_only: bool, pub_only: bool,
_capture: Option<Scope>, _capture: Option<Scope>,
def_val: Option<bool>, def_val: Option<bool>,
level: usize, _level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
// Check for data race. // Check for data race.
if cfg!(not(feature = "no_closure")) { if cfg!(not(feature = "no_closure")) {
@ -479,20 +486,22 @@ impl Engine {
KEYWORD_FN_PTR KEYWORD_FN_PTR
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) => if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
{ {
Err(Box::new(EvalAltResult::ErrorRuntime( EvalAltResult::ErrorRuntime(
"'Fn' should not be called in method style. Try Fn(...);".into(), "'Fn' should not be called in method style. Try Fn(...);".into(),
Position::none(), Position::none(),
))) )
.into()
} }
// eval - reaching this point it must be a method-style call // eval - reaching this point it must be a method-style call
KEYWORD_EVAL KEYWORD_EVAL
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) => if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
{ {
Err(Box::new(EvalAltResult::ErrorRuntime( EvalAltResult::ErrorRuntime(
"'eval' should not be called in method style. Try eval(...);".into(), "'eval' should not be called in method style. Try eval(...);".into(),
Position::none(), Position::none(),
))) )
.into()
} }
// Normal script function call // Normal script function call
@ -510,7 +519,7 @@ impl Engine {
add_captured_variables_into_scope(&func.externals, captured, scope); add_captured_variables_into_scope(&func.externals, captured, scope);
} }
let result = if is_method { let result = if _is_method {
// Method call of script function - map first argument to `this` // Method call of script function - map first argument to `this`
let (first, rest) = args.split_at_mut(1); let (first, rest) = args.split_at_mut(1);
self.call_script_fn( self.call_script_fn(
@ -522,7 +531,7 @@ impl Engine {
fn_name, fn_name,
func, func,
rest, rest,
level, _level,
)? )?
} else { } else {
// Normal call of script function - map first argument to `this` // Normal call of script function - map first argument to `this`
@ -531,7 +540,7 @@ impl Engine {
backup.change_first_arg_to_copy(is_ref, args); backup.change_first_arg_to_copy(is_ref, args);
let result = self.call_script_fn( let result = self.call_script_fn(
scope, mods, state, lib, &mut None, fn_name, func, args, level, scope, mods, state, lib, &mut None, fn_name, func, args, _level,
); );
// Restore the original reference // Restore the original reference
@ -694,12 +703,12 @@ impl Engine {
&& _fn_name == KEYWORD_IS_SHARED && _fn_name == KEYWORD_IS_SHARED
&& idx.is_empty() && idx.is_empty()
{ {
// take call // is_shared call
Ok((target.is_shared().into(), false)) Ok((target.is_shared().into(), false))
} else { } else {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
let redirected; let redirected;
let mut _hash = hash_script; let mut hash = hash_script;
// 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"))]
@ -719,7 +728,7 @@ impl Engine {
.for_each(|(i, v)| idx.insert(i, v)); .for_each(|(i, v)| idx.insert(i, v));
} }
// Recalculate the hash based on the new function name and new arguments // Recalculate the hash based on the new function name and new arguments
_hash = if native { hash = if native {
0 0
} else { } else {
calc_fn_hash(empty(), _fn_name, idx.len(), empty()) calc_fn_hash(empty(), _fn_name, idx.len(), empty())
@ -729,7 +738,7 @@ impl Engine {
}; };
if native { if native {
_hash = 0; hash = 0;
} }
// Attached object pointer in front of the arguments // Attached object pointer in front of the arguments
@ -737,7 +746,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, _hash, args, is_ref, true, pub_only, None, def_val, level, state, lib, _fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
) )
}?; }?;
@ -780,11 +789,12 @@ impl Engine {
return arg_value return arg_value
.take_immutable_string() .take_immutable_string()
.map_err(|typ| { .map_err(|typ| {
Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(), self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(), typ.into(),
expr.position(), expr.position(),
)) )
.into()
}) })
.and_then(|s| FnPtr::try_from(s)) .and_then(|s| FnPtr::try_from(s))
.map(Into::<Dynamic>::into) .map(Into::<Dynamic>::into)
@ -798,11 +808,12 @@ impl Engine {
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
if !fn_ptr.is::<FnPtr>() { if !fn_ptr.is::<FnPtr>() {
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<FnPtr>()).into(), self.map_type_name(type_name::<FnPtr>()).into(),
self.map_type_name(fn_ptr.type_name()).into(), self.map_type_name(fn_ptr.type_name()).into(),
expr.position(), expr.position(),
))); )
.into();
} }
let (fn_name, fn_curry) = fn_ptr.cast::<FnPtr>().take_data(); let (fn_name, fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
@ -852,11 +863,12 @@ impl Engine {
// Recalculate hash // Recalculate hash
hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty()); hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
} else { } else {
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<FnPtr>()).into(), self.map_type_name(type_name::<FnPtr>()).into(),
fn_name.type_name().into(), fn_name.type_name().into(),
expr.position(), expr.position(),
))); )
.into();
} }
} }
@ -1045,7 +1057,7 @@ impl Engine {
} }
Some(f) => f.get_native_fn()(self, lib, args.as_mut()), Some(f) => f.get_native_fn()(self, lib, args.as_mut()),
None if def_val.is_some() => Ok(def_val.unwrap().into()), None if def_val.is_some() => Ok(def_val.unwrap().into()),
None => Err(Box::new(EvalAltResult::ErrorFunctionNotFound( None => EvalAltResult::ErrorFunctionNotFound(
format!( format!(
"{}{} ({})", "{}{} ({})",
modules, modules,
@ -1060,7 +1072,8 @@ impl Engine {
.join(", ") .join(", ")
), ),
Position::none(), Position::none(),
))), )
.into(),
} }
} }
} }

View File

@ -1,21 +1,20 @@
//! Module defining interfaces to native-Rust functions. //! Module defining interfaces to native-Rust functions.
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::Engine; use crate::engine::Engine;
use crate::module::Module; use crate::module::Module;
use crate::parser::FnAccess; use crate::parser::{FnAccess, 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; use crate::utils::ImmutableString;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::{module::FuncReturn, parser::ScriptFnDef, utils::StaticVec}; use crate::{calc_fn_hash, module::FuncReturn, utils::StaticVec};
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, string::String, vec::Vec}; use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::stdlib::mem; use crate::stdlib::{iter::empty, mem};
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
use crate::stdlib::rc::Rc; use crate::stdlib::rc::Rc;
@ -176,10 +175,7 @@ impl TryFrom<ImmutableString> for FnPtr {
if is_valid_identifier(value.chars()) { if is_valid_identifier(value.chars()) {
Ok(Self(value, Default::default())) Ok(Self(value, Default::default()))
} else { } else {
Err(Box::new(EvalAltResult::ErrorFunctionNotFound( EvalAltResult::ErrorFunctionNotFound(value.into(), Position::none()).into()
value.into(),
Position::none(),
)))
} }
} }
} }
@ -293,10 +289,11 @@ impl CallableFunction {
} }
} }
/// Is this a Rhai-scripted function? /// Is this a Rhai-scripted function?
#[cfg(not(feature = "no_function"))]
pub fn is_script(&self) -> bool { pub fn is_script(&self) -> bool {
match self { match self {
#[cfg(not(feature = "no_function"))]
Self::Script(_) => true, Self::Script(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false,
} }
} }
@ -314,6 +311,8 @@ impl CallableFunction {
pub fn access(&self) -> FnAccess { pub fn access(&self) -> FnAccess {
match self { match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public,
#[cfg(not(feature = "no_function"))]
Self::Script(f) => f.access, Self::Script(f) => f.access,
} }
} }
@ -348,10 +347,11 @@ impl CallableFunction {
/// # Panics /// # Panics
/// ///
/// Panics if the `CallableFunction` is not `Script`. /// Panics if the `CallableFunction` is not `Script`.
#[cfg(not(feature = "no_function"))]
pub fn get_fn_def(&self) -> &ScriptFnDef { pub fn get_fn_def(&self) -> &ScriptFnDef {
match self { match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(), Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(),
#[cfg(not(feature = "no_function"))]
Self::Script(f) => f, Self::Script(f) => f,
} }
} }
@ -385,16 +385,22 @@ impl From<IteratorFn> for CallableFunction {
} }
} }
#[cfg(not(feature = "no_function"))]
impl From<ScriptFnDef> for CallableFunction { impl From<ScriptFnDef> for CallableFunction {
fn from(func: ScriptFnDef) -> Self { fn from(_func: ScriptFnDef) -> Self {
Self::Script(func.into()) #[cfg(feature = "no_function")]
unreachable!();
#[cfg(not(feature = "no_function"))]
Self::Script(_func.into())
} }
} }
#[cfg(not(feature = "no_function"))]
impl From<Shared<ScriptFnDef>> for CallableFunction { impl From<Shared<ScriptFnDef>> for CallableFunction {
fn from(func: Shared<ScriptFnDef>) -> Self { fn from(_func: Shared<ScriptFnDef>) -> Self {
Self::Script(func) #[cfg(feature = "no_function")]
unreachable!();
#[cfg(not(feature = "no_function"))]
Self::Script(_func)
} }
} }

View File

@ -4,13 +4,13 @@ use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::Engine; use crate::engine::Engine;
use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync}; use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync};
use crate::parser::{FnAccess, FnAccess::Public}; use crate::parser::{FnAccess, FnAccess::Public, ScriptFnDef};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
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"))] #[cfg(not(feature = "no_function"))]
use crate::{fn_native::Shared, parser::ScriptFnDef}; use crate::fn_native::Shared;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::{ use crate::{
@ -248,17 +248,13 @@ impl Module {
hash_var: u64, hash_var: u64,
) -> Result<&mut Dynamic, Box<EvalAltResult>> { ) -> Result<&mut Dynamic, Box<EvalAltResult>> {
self.all_variables.get_mut(&hash_var).ok_or_else(|| { self.all_variables.get_mut(&hash_var).ok_or_else(|| {
Box::new(EvalAltResult::ErrorVariableNotFound( EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()
String::new(),
Position::none(),
))
}) })
} }
/// Set a script-defined function into the module. /// Set a script-defined function into the module.
/// ///
/// If there is an existing function of the same name and number of arguments, it is replaced. /// If there is an existing function of the same name and number of arguments, it is replaced.
#[cfg(not(feature = "no_function"))]
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> &mut Self { pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> &mut Self {
// None + function name + number of arguments. // None + function name + number of arguments.
let hash_script = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty()); let hash_script = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty());
@ -1003,7 +999,6 @@ 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)> {
@ -1519,7 +1514,7 @@ mod stat {
self.0 self.0
.get(path) .get(path)
.cloned() .cloned()
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos))) .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
} }
} }
} }
@ -1599,10 +1594,7 @@ mod collection {
} }
} }
Err(Box::new(EvalAltResult::ErrorModuleNotFound( EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
path.into(),
pos,
)))
} }
} }
} }

View File

@ -7,13 +7,10 @@ use crate::engine::{
}; };
use crate::fn_native::FnPtr; use crate::fn_native::FnPtr;
use crate::module::Module; use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST}; use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
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;
@ -46,10 +43,12 @@ impl OptimizationLevel {
self == Self::None self == Self::None
} }
/// Is the `OptimizationLevel` Simple. /// Is the `OptimizationLevel` Simple.
#[cfg(not(feature = "no_optimize"))]
pub fn is_simple(self) -> bool { pub fn is_simple(self) -> bool {
self == Self::Simple self == Self::Simple
} }
/// Is the `OptimizationLevel` Full. /// Is the `OptimizationLevel` Full.
#[cfg(not(feature = "no_optimize"))]
pub fn is_full(self) -> bool { pub fn is_full(self) -> bool {
self == Self::Full self == Self::Full
} }

View File

@ -17,7 +17,6 @@ use num_traits::{
use num_traits::float::Float; use num_traits::float::Float;
use crate::stdlib::{ use crate::stdlib::{
boxed::Box,
fmt::Display, fmt::Display,
format, format,
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub},
@ -26,28 +25,31 @@ use crate::stdlib::{
// Checked add // Checked add
pub fn add<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> { pub 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( EvalAltResult::ErrorArithmetic(
format!("Addition overflow: {} + {}", x, y), format!("Addition overflow: {} + {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// Checked subtract // Checked subtract
pub fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> { pub 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( EvalAltResult::ErrorArithmetic(
format!("Subtraction underflow: {} - {}", x, y), format!("Subtraction underflow: {} - {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// Checked multiply // Checked multiply
pub fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> { pub 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( EvalAltResult::ErrorArithmetic(
format!("Multiplication overflow: {} * {}", x, y), format!("Multiplication overflow: {} * {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// Checked divide // Checked divide
@ -57,26 +59,26 @@ where
{ {
// Detect division by zero // Detect division by zero
if y == T::zero() { if y == T::zero() {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Division by zero: {} / {}", x, y), format!("Division by zero: {} / {}", x, y),
Position::none(), Position::none(),
))); )
.into();
} }
x.checked_div(&y).ok_or_else(|| { x.checked_div(&y).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Division overflow: {} / {}", x, y), format!("Division overflow: {} / {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX // Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
pub fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> { pub 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( EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none())
format!("Negation overflow: -{}", x), .into()
Position::none(),
))
}) })
} }
// Checked absolute // Checked absolute
@ -87,10 +89,8 @@ pub fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> FuncReturn<T> {
Ok(x) Ok(x)
} else { } else {
x.checked_neg().ok_or_else(|| { x.checked_neg().ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none())
format!("Negation overflow: -{}", x), .into()
Position::none(),
))
}) })
} }
} }
@ -140,34 +140,38 @@ fn binary_xor<T: BitXor>(x: T, y: T) -> FuncReturn<<T as BitXor>::Output> {
pub fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> { pub 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 {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Left-shift by a negative number: {} << {}", x, y), format!("Left-shift by a negative number: {} << {}", x, y),
Position::none(), Position::none(),
))); )
.into();
} }
CheckedShl::checked_shl(&x, y as u32).ok_or_else(|| { CheckedShl::checked_shl(&x, y as u32).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Left-shift by too many bits: {} << {}", x, y), format!("Left-shift by too many bits: {} << {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// Checked right-shift // Checked right-shift
pub fn shr<T: Display + CheckedShr>(x: T, y: INT) -> FuncReturn<T> { pub 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 {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Right-shift by a negative number: {} >> {}", x, y), format!("Right-shift by a negative number: {} >> {}", x, y),
Position::none(), Position::none(),
))); )
.into();
} }
CheckedShr::checked_shr(&x, y as u32).ok_or_else(|| { CheckedShr::checked_shr(&x, y as u32).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Right-shift by too many bits: {} % {}", x, y), format!("Right-shift by too many bits: {} % {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// 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
@ -181,10 +185,11 @@ pub fn shr_u<T: Shr<T>>(x: T, y: T) -> FuncReturn<<T as Shr<T>>::Output> {
// Checked modulo // Checked modulo
pub fn modulo<T: Display + CheckedRem>(x: T, y: T) -> FuncReturn<T> { pub 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( EvalAltResult::ErrorArithmetic(
format!("Modulo division by zero or overflow: {} % {}", x, y), format!("Modulo division by zero or overflow: {} % {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
// Unchecked modulo - may panic if dividing by zero // Unchecked modulo - may panic if dividing by zero
@ -195,35 +200,40 @@ fn modulo_u<T: Rem>(x: T, y: T) -> FuncReturn<<T as Rem>::Output> {
pub fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> { pub fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> {
if cfg!(not(feature = "only_i32")) { if cfg!(not(feature = "only_i32")) {
if y > (u32::MAX as INT) { if y > (u32::MAX as INT) {
Err(Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Integer raised to too large an index: {} ~ {}", x, y), format!("Integer raised to too large an index: {} ~ {}", x, y),
Position::none(), Position::none(),
))) )
.into()
} else if y < 0 { } else if y < 0 {
Err(Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Integer raised to a negative index: {} ~ {}", x, y), format!("Integer raised to a negative index: {} ~ {}", x, y),
Position::none(), Position::none(),
))) )
.into()
} else { } else {
x.checked_pow(y as u32).ok_or_else(|| { x.checked_pow(y as u32).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y), format!("Power overflow: {} ~ {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
} else { } else {
if y < 0 { if y < 0 {
Err(Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Integer raised to a negative index: {} ~ {}", x, y), format!("Integer raised to a negative index: {} ~ {}", x, y),
Position::none(), Position::none(),
))) )
.into()
} else { } else {
x.checked_pow(y as u32).ok_or_else(|| { x.checked_pow(y as u32).ok_or_else(|| {
Box::new(EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y), format!("Power overflow: {} ~ {}", x, y),
Position::none(), Position::none(),
)) )
.into()
}) })
} }
} }
@ -242,10 +252,11 @@ pub fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn<FLOAT> {
pub fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn<FLOAT> { pub 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) {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Number raised to too large an index: {} ~ {}", x, y), format!("Number raised to too large an index: {} ~ {}", x, y),
Position::none(), Position::none(),
))); )
.into();
} }
Ok(x.powi(y as i32)) Ok(x.powi(y as i32))

View File

@ -43,12 +43,13 @@ fn pad<T: Variant + Clone>(
&& len > 0 && len > 0
&& (len as usize) > _engine.limits.max_array_size && (len as usize) > _engine.limits.max_array_size
{ {
return Err(Box::new(EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(), "Size of array".to_string(),
_engine.limits.max_array_size, _engine.limits.max_array_size,
len as usize, len as usize,
Position::none(), Position::none(),
))); )
.into();
} }
if len > 0 { if len > 0 {

View File

@ -12,7 +12,7 @@ use crate::{result::EvalAltResult, token::Position};
use num_traits::float::Float; use num_traits::float::Float;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::stdlib::{boxed::Box, format}; use crate::stdlib::format;
#[allow(dead_code)] #[allow(dead_code)]
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
@ -109,10 +109,10 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
"to_int", "to_int",
|x: f32| { |x: f32| {
if x > (MAX_INT as f32) { if x > (MAX_INT as f32) {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x), format!("Integer overflow: to_int({})", x),
Position::none(), Position::none(),
))); ).into();
} }
Ok(x.trunc() as INT) Ok(x.trunc() as INT)
@ -122,10 +122,10 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
"to_int", "to_int",
|x: FLOAT| { |x: FLOAT| {
if x > (MAX_INT as FLOAT) { if x > (MAX_INT as FLOAT) {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x), format!("Integer overflow: to_int({})", x),
Position::none(), Position::none(),
))); ).into();
} }
Ok(x.trunc() as INT) Ok(x.trunc() as INT)

View File

@ -231,12 +231,12 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
// 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.limits.max_string_size > 0 && len > 0 && (len as usize) > _engine.limits.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 EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
_engine.limits.max_string_size, _engine.limits.max_string_size,
len as usize, len as usize,
Position::none(), Position::none(),
))); ).into();
} }
if len > 0 { if len > 0 {
@ -254,12 +254,12 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _engine.limits.max_string_size > 0 && s.len() > _engine.limits.max_string_size { if _engine.limits.max_string_size > 0 && s.len() > _engine.limits.max_string_size {
return Err(Box::new(EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
_engine.limits.max_string_size, _engine.limits.max_string_size,
s.len(), s.len(),
Position::none(), Position::none(),
))); ).into();
} }
} }
} }

View File

@ -41,13 +41,13 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if seconds > (MAX_INT as u64) { if seconds > (MAX_INT as u64) {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!( format!(
"Integer overflow for timestamp duration: {}", "Integer overflow for timestamp duration: {}",
-(seconds as i64) -(seconds as i64)
), ),
Position::none(), Position::none(),
))); ).into();
} }
return Ok(-(seconds as INT)); return Ok(-(seconds as INT));
@ -62,10 +62,10 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if seconds > (MAX_INT as u64) { if seconds > (MAX_INT as u64) {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Integer overflow for timestamp duration: {}", seconds), format!("Integer overflow for timestamp duration: {}", seconds),
Position::none(), Position::none(),
))); ).into();
} }
return Ok(seconds as INT); return Ok(seconds as INT);
@ -92,10 +92,10 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if seconds > (MAX_INT as u64) { if seconds > (MAX_INT as u64) {
return Err(Box::new(EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Integer overflow for timestamp.elapsed: {}", seconds), format!("Integer overflow for timestamp.elapsed: {}", seconds),
Position::none(), Position::none(),
))); ).into();
} }
Ok(seconds as INT) Ok(seconds as INT)

View File

@ -2,9 +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};
Engine, KEYWORD_FN_PTR_CURRY, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT,
};
use crate::error::{LexError, ParseError, ParseErrorType}; use crate::error::{LexError, ParseError, ParseErrorType};
use crate::fn_native::{FnPtr, Shared}; use crate::fn_native::{FnPtr, Shared};
use crate::module::{Module, ModuleRef}; use crate::module::{Module, ModuleRef};
@ -15,7 +13,7 @@ use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, To
use crate::utils::{StaticVec, StraightHasherBuilder}; use crate::utils::{StaticVec, StraightHasherBuilder};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::engine::FN_ANONYMOUS; use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY};
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::engine::{make_getter, make_setter}; use crate::engine::{make_getter, make_setter};
@ -570,7 +568,7 @@ pub enum Stmt {
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>, Position)>), ReturnWithVal(Box<((ReturnType, Position), Option<Expr>, Position)>),
/// import expr as module /// import expr as module
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import(Box<(Expr, (String, Position), Position)>), Import(Box<(Expr, Option<(String, Position)>, Position)>),
/// expr id as name, ... /// expr id as name, ...
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export( Export(
@ -2687,14 +2685,8 @@ fn parse_import(
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
// import expr as ... // import expr as ...
match input.next().unwrap() { if !match_token(input, Token::As)? {
(Token::As, _) => (), return Ok(Stmt::Import(Box::new((expr, None, token_pos))));
(_, pos) => {
return Err(
PERR::MissingToken(Token::As.into(), "in this import statement".into())
.into_err(pos),
)
}
} }
// import expr as name ... // import expr as name ...
@ -2711,7 +2703,7 @@ fn parse_import(
Ok(Stmt::Import(Box::new(( Ok(Stmt::Import(Box::new((
expr, expr,
(name, settings.pos), Some((name, settings.pos)),
token_pos, token_pos,
)))) ))))
} }
@ -3135,6 +3127,7 @@ fn parse_fn(
} }
/// Creates a curried expression from a list of external variables /// Creates a curried expression from a list of external variables
#[cfg(not(feature = "no_function"))]
fn make_curry_from_externals( fn make_curry_from_externals(
fn_expr: Expr, fn_expr: Expr,
externals: StaticVec<(String, Position)>, externals: StaticVec<(String, Position)>,

View File

@ -361,3 +361,9 @@ impl EvalAltResult {
self self
} }
} }
impl<T> From<EvalAltResult> for Result<T, Box<EvalAltResult>> {
fn from(err: EvalAltResult) -> Self {
Err(err.into())
}
}

View File

@ -53,11 +53,12 @@ impl<'de> DynamicDeserializer<'de> {
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> { fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
Err(Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
error.into(), error.into(),
self.value.type_name().into(), self.value.type_name().into(),
Position::none(), Position::none(),
))) )
.into()
} }
fn deserialize_int<V: Visitor<'de>>( fn deserialize_int<V: Visitor<'de>>(
&mut self, &mut self,
@ -134,10 +135,8 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
impl Error for Box<EvalAltResult> { impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
Box::new(EvalAltResult::ErrorParsing( EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none())
ParseErrorType::BadInput(err.to_string()), .into()
Position::none(),
))
} }
} }

View File

@ -85,9 +85,9 @@ impl DynamicSerializer {
/// assert!(value.is::<Map>()); /// assert!(value.is::<Map>());
/// ///
/// let map = value.cast::<Map>(); /// let map = value.cast::<Map>();
/// let point = map.get("d").unwrap().downcast_ref::<Map>().unwrap(); /// let point = map["d"].read_lock::<Map>().unwrap();
/// assert_eq!(*point.get("x").unwrap().downcast_ref::<f64>().unwrap(), 123.456); /// assert_eq!(*point["x"].read_lock::<f64>().unwrap(), 123.456);
/// assert_eq!(*point.get("y").unwrap().downcast_ref::<f64>().unwrap(), 999.0); /// assert_eq!(*point["y"].read_lock::<f64>().unwrap(), 999.0);
/// # } /// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -99,10 +99,7 @@ pub fn to_dynamic<T: Serialize>(value: T) -> Result<Dynamic, Box<EvalAltResult>>
impl Error for Box<EvalAltResult> { impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
Box::new(EvalAltResult::ErrorRuntime( EvalAltResult::ErrorRuntime(err.to_string(), Position::none()).into()
err.to_string(),
Position::none(),
))
} }
} }
@ -298,22 +295,24 @@ impl Serializer for &mut DynamicSerializer {
make_variant(_variant, content) make_variant(_variant, content)
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(), "Dynamic".into(),
"map".into(), "map".into(),
Position::none(), Position::none(),
))); )
.into();
} }
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Box<EvalAltResult>> { fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(DynamicSerializer::new(Array::new().into())); return Ok(DynamicSerializer::new(Array::new().into()));
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(), "Dynamic".into(),
"array".into(), "array".into(),
Position::none(), Position::none(),
))); )
.into();
} }
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Box<EvalAltResult>> { fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Box<EvalAltResult>> {
@ -346,11 +345,12 @@ impl Serializer for &mut DynamicSerializer {
let err_type = "map"; let err_type = "map";
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
let err_type = "array"; let err_type = "array";
Err(Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(), "Dynamic".into(),
err_type.into(), err_type.into(),
Position::none(), Position::none(),
))) )
.into()
} }
} }
@ -358,11 +358,12 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(DynamicSerializer::new(Map::new().into())); return Ok(DynamicSerializer::new(Map::new().into()));
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(), "Dynamic".into(),
"map".into(), "map".into(),
Position::none(), Position::none(),
))); )
.into();
} }
fn serialize_struct( fn serialize_struct(
@ -386,11 +387,12 @@ impl Serializer for &mut DynamicSerializer {
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 EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(), "Dynamic".into(),
"map".into(), "map".into(),
Position::none(), Position::none(),
))); )
.into();
} }
} }
@ -499,11 +501,11 @@ impl SerializeMap for DynamicSerializer {
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( 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();
@ -523,11 +525,11 @@ impl SerializeMap for DynamicSerializer {
{ {
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( 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();

View File

@ -20,11 +20,12 @@ impl<'a> ImmutableStringDeserializer<'a> {
} }
/// 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>> {
Err(Box::new(EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
type_name::<T>().into(), type_name::<T>().into(),
"string".into(), "string".into(),
Position::none(), Position::none(),
))) )
.into()
} }
} }

View File

@ -1,13 +1,15 @@
//! Configuration settings for `Engine`. //! Configuration settings for `Engine`.
use crate::engine::Engine; use crate::engine::Engine;
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};
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::module::ModuleResolver; use crate::module::ModuleResolver;
#[cfg(not(feature = "no_optimize"))]
use crate::optimize::OptimizationLevel;
use crate::stdlib::{format, string::String}; use crate::stdlib::{format, string::String};
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]

View File

@ -680,6 +680,7 @@ impl Token {
} }
/// Convert a token into a function name, if possible. /// Convert a token into a function name, if possible.
#[cfg(not(feature = "no_function"))]
pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> { pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> {
match self { match self {
Self::Reserved(s) if can_override_keyword(&s) => Ok(s), Self::Reserved(s) if can_override_keyword(&s) => Ok(s),
@ -1442,6 +1443,7 @@ pub fn is_keyword_function(name: &str) -> bool {
} }
/// Can this keyword be overridden as a function? /// Can this keyword be overridden as a function?
#[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn can_override_keyword(name: &str) -> bool { pub fn can_override_keyword(name: &str) -> bool {
match name { match name {

View File

@ -83,6 +83,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
#[test] #[test]
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "sync"))]
fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> { fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();

View File

@ -5,16 +5,16 @@ use std::sync::{Arc, Mutex, RwLock};
/// Simulate a command object. /// Simulate a command object.
struct Command { struct Command {
/// Simulate an external state. /// Simulate an external state.
state: i64, state: INT,
} }
impl Command { impl Command {
/// Do some action. /// Do some action.
pub fn action(&mut self, val: i64) { pub fn action(&mut self, val: INT) {
self.state = val; self.state = val;
} }
/// Get current value. /// Get current value.
pub fn get(&self) -> i64 { pub fn get(&self) -> INT {
self.state self.state
} }
} }
@ -39,7 +39,7 @@ fn test_side_effects_command() -> Result<(), Box<EvalAltResult>> {
// Register type. // Register type.
engine.register_type_with_name::<API>("CommandType"); engine.register_type_with_name::<API>("CommandType");
engine.register_fn("action", |api: &mut API, x: i64| { engine.register_fn("action", |api: &mut API, x: INT| {
let mut command = api.lock().unwrap(); let mut command = api.lock().unwrap();
let val = command.get(); let val = command.get();
command.action(val + x); command.action(val + x);

View File

@ -1,5 +1,5 @@
#![cfg(not(feature = "unchecked"))] #![cfg(not(feature = "unchecked"))]
use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType}; use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType, INT};
#[test] #[test]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -7,7 +7,7 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<i64>( engine.eval::<INT>(
r" r"
fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } } fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } }
foo(8) foo(8)