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

View File

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

View File

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

View File

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

View File

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

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'
```
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Number of Parameters
--------------------
@ -51,8 +53,8 @@ The following table illustrates the differences:
| 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,
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
-----------------
A module can be _imported_ via the `import` statement, and its members are accessed via '`::`' similar to C++.
A module can be _imported_ via the `import` statement, and be given a name.
Its members can be accessed via '`::`' similar to C++.
A module that is only `import`-ed but not under any module name is commonly used for initialization purposes,
where the module script contains initialization statements that puts the functions registered with the
[`Engine`] into a particular state.
```rust
import "crypto" as lock; // import the script file 'crypto.rhai' as a module named 'lock'
import "crypto_init"; // run the script file 'crypto_init.rhai' without creating an imported module
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
lock::encrypt(secret); // use functions defined under the module via '::'

View File

@ -83,6 +83,8 @@ engine
***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).*
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
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()")?;
```
[`type_of()`]
-------------
`type_of()` a Custom 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
```
**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);
Ok(module)
} 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.
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,
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
------------------------
@ -152,9 +171,9 @@ to partition the slice:
let (first, rest) = args.split_at_mut(1);
// 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
// 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(feature = "sync")]
Union::Shared(cell) => fmt::Display::fmt(*cell.read_lock().unwrap(), f),
Union::Shared(cell) => fmt::Display::fmt(&*cell.read().unwrap(), f),
}
}
}
@ -448,7 +448,7 @@ impl fmt::Debug for Dynamic {
}
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
Union::Shared(cell) => fmt::Display::fmt(*cell.read_lock().unwrap(), f),
Union::Shared(cell) => fmt::Debug::fmt(&*cell.read().unwrap(), f),
}
}
}

View File

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

View File

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

View File

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

View File

@ -1,21 +1,20 @@
//! Module defining interfaces to native-Rust functions.
use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::Engine;
use crate::module::Module;
use crate::parser::FnAccess;
use crate::parser::{FnAccess, ScriptFnDef};
use crate::result::EvalAltResult;
use crate::token::{is_valid_identifier, Position};
use crate::utils::ImmutableString;
#[cfg(not(feature = "no_function"))]
use crate::{module::FuncReturn, parser::ScriptFnDef, utils::StaticVec};
use crate::{calc_fn_hash, module::FuncReturn, utils::StaticVec};
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, string::String, vec::Vec};
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec};
#[cfg(not(feature = "no_function"))]
use crate::stdlib::mem;
use crate::stdlib::{iter::empty, mem};
#[cfg(not(feature = "sync"))]
use crate::stdlib::rc::Rc;
@ -176,10 +175,7 @@ impl TryFrom<ImmutableString> for FnPtr {
if is_valid_identifier(value.chars()) {
Ok(Self(value, Default::default()))
} else {
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
value.into(),
Position::none(),
)))
EvalAltResult::ErrorFunctionNotFound(value.into(), Position::none()).into()
}
}
}
@ -293,10 +289,11 @@ impl CallableFunction {
}
}
/// Is this a Rhai-scripted function?
#[cfg(not(feature = "no_function"))]
pub fn is_script(&self) -> bool {
match self {
#[cfg(not(feature = "no_function"))]
Self::Script(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false,
}
}
@ -314,6 +311,8 @@ impl CallableFunction {
pub fn access(&self) -> FnAccess {
match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public,
#[cfg(not(feature = "no_function"))]
Self::Script(f) => f.access,
}
}
@ -348,10 +347,11 @@ impl CallableFunction {
/// # Panics
///
/// Panics if the `CallableFunction` is not `Script`.
#[cfg(not(feature = "no_function"))]
pub fn get_fn_def(&self) -> &ScriptFnDef {
match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(),
#[cfg(not(feature = "no_function"))]
Self::Script(f) => f,
}
}
@ -385,16 +385,22 @@ impl From<IteratorFn> for CallableFunction {
}
}
#[cfg(not(feature = "no_function"))]
impl From<ScriptFnDef> for CallableFunction {
fn from(func: ScriptFnDef) -> Self {
Self::Script(func.into())
fn from(_func: ScriptFnDef) -> Self {
#[cfg(feature = "no_function")]
unreachable!();
#[cfg(not(feature = "no_function"))]
Self::Script(_func.into())
}
}
#[cfg(not(feature = "no_function"))]
impl From<Shared<ScriptFnDef>> for CallableFunction {
fn from(func: Shared<ScriptFnDef>) -> Self {
Self::Script(func)
fn from(_func: Shared<ScriptFnDef>) -> Self {
#[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::engine::Engine;
use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync};
use crate::parser::{FnAccess, FnAccess::Public};
use crate::parser::{FnAccess, FnAccess::Public, ScriptFnDef};
use crate::result::EvalAltResult;
use crate::token::{Position, Token};
use crate::utils::{StaticVec, StraightHasherBuilder};
#[cfg(not(feature = "no_function"))]
use crate::{fn_native::Shared, parser::ScriptFnDef};
use crate::fn_native::Shared;
#[cfg(not(feature = "no_module"))]
use crate::{
@ -248,17 +248,13 @@ impl Module {
hash_var: u64,
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
self.all_variables.get_mut(&hash_var).ok_or_else(|| {
Box::new(EvalAltResult::ErrorVariableNotFound(
String::new(),
Position::none(),
))
EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()
})
}
/// Set a script-defined function into the module.
///
/// If there is an existing function of the same name and number of arguments, it is replaced.
#[cfg(not(feature = "no_function"))]
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> &mut Self {
// None + function name + number of arguments.
let hash_script = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty());
@ -1003,7 +999,6 @@ impl Module {
}
/// Get an iterator to the functions in the module.
#[cfg(not(feature = "no_function"))]
pub(crate) fn iter_fn(
&self,
) -> impl Iterator<Item = &(String, FnAccess, StaticVec<TypeId>, Func)> {
@ -1519,7 +1514,7 @@ mod stat {
self.0
.get(path)
.cloned()
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos)))
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
}
}
}
@ -1599,10 +1594,7 @@ mod collection {
}
}
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
path.into(),
pos,
)))
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -361,3 +361,9 @@ impl EvalAltResult {
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.
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
EvalAltResult::ErrorMismatchOutputType(
error.into(),
self.value.type_name().into(),
Position::none(),
)))
)
.into()
}
fn deserialize_int<V: Visitor<'de>>(
&mut self,
@ -134,10 +135,8 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self {
Box::new(EvalAltResult::ErrorParsing(
ParseErrorType::BadInput(err.to_string()),
Position::none(),
))
EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none())
.into()
}
}

View File

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

View File

@ -20,11 +20,12 @@ impl<'a> ImmutableStringDeserializer<'a> {
}
/// Shortcut for a type conversion error.
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
EvalAltResult::ErrorMismatchOutputType(
type_name::<T>().into(),
"string".into(),
Position::none(),
)))
)
.into()
}
}

View File

@ -1,13 +1,15 @@
//! Configuration settings for `Engine`.
use crate::engine::Engine;
use crate::optimize::OptimizationLevel;
use crate::packages::PackageLibrary;
use crate::token::{is_valid_identifier, Token};
#[cfg(not(feature = "no_module"))]
use crate::module::ModuleResolver;
#[cfg(not(feature = "no_optimize"))]
use crate::optimize::OptimizationLevel;
use crate::stdlib::{format, string::String};
#[cfg(not(feature = "no_module"))]

View File

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

View File

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

View File

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

View File

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