diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db8a63d9..8c8b19eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index f6ffdea4..df785e3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/RELEASES.md b/RELEASES.md index 23973fe7..87cb5bdb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -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. diff --git a/doc/src/context.json b/doc/src/context.json index 16f803db..792990b1 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -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": "", diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index 8f308106..8d0820c8 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -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; diff --git a/doc/src/language/method.md b/doc/src/language/method.md index 290d1a06..3b8a82d4 100644 --- a/doc/src/language/method.md +++ b/doc/src/language/method.md @@ -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. diff --git a/doc/src/language/modules/import.md b/doc/src/language/modules/import.md index 5e0a377a..b592ff74 100644 --- a/doc/src/language/modules/import.md +++ b/doc/src/language/modules/import.md @@ -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 '::' diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index 3e6b5ca3..b2325204 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -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. diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md index 4c44399d..7597e8d5 100644 --- a/doc/src/rust/getters-setters.md +++ b/doc/src/rust/getters-setters.md @@ -42,3 +42,5 @@ let result = engine.eval::(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.** diff --git a/doc/src/rust/modules/imp-resolver.md b/doc/src/rust/modules/imp-resolver.md index 55d68315..d1d9ed38 100644 --- a/doc/src/rust/modules/imp-resolver.md +++ b/doc/src/rust/modules/imp-resolver.md @@ -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()) } } } diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index b666d588..b7069ce3 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -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::(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>` (`Arc>` 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::().unwrap(); +let this_ptr: &mut Dynamic = &mut *first[0].write_lock::().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::().unwrap(); +let value_ref: &Dynamic = &*rest[0].read_lock::().unwrap(); ``` diff --git a/src/any.rs b/src/any.rs index aa218127..318b97d1 100644 --- a/src/any.rs +++ b/src/any.rs @@ -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), } } } diff --git a/src/api.rs b/src/api.rs index 43aaf6d5..34af07a1 100644 --- a/src/api.rs +++ b/src/api.rs @@ -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> { 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::().ok_or_else(|| { - Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).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::()).into(), typ.into(), Position::none(), - )) + ) + .into() }); } @@ -1271,13 +1272,8 @@ impl Engine { ) -> FuncReturn { 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(); diff --git a/src/engine.rs b/src/engine.rs index 65228a8f..9a771ac6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -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::() => { @@ -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(); } } diff --git a/src/fn_call.rs b/src/fn_call.rs index c7d9050c..20557748 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -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, 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::()).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::()).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, def_val: Option, - level: usize, + _level: usize, ) -> Result<(Dynamic, bool), Box> { // 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::()).into(), typ.into(), expr.position(), - )) + ) + .into() }) .and_then(|s| FnPtr::try_from(s)) .map(Into::::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::() { - return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + return EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), self.map_type_name(fn_ptr.type_name()).into(), expr.position(), - ))); + ) + .into(); } let (fn_name, fn_curry) = fn_ptr.cast::().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::()).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(), } } } diff --git a/src/fn_native.rs b/src/fn_native.rs index a972793b..cc3ed0dc 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -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 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 for CallableFunction { } } -#[cfg(not(feature = "no_function"))] impl From 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> for CallableFunction { - fn from(func: Shared) -> Self { - Self::Script(func) + fn from(_func: Shared) -> Self { + #[cfg(feature = "no_function")] + unreachable!(); + + #[cfg(not(feature = "no_function"))] + Self::Script(_func) } } diff --git a/src/module.rs b/src/module.rs index fa23abd6..b3bd5598 100644 --- a/src/module.rs +++ b/src/module.rs @@ -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> { 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, 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() } } } diff --git a/src/optimize.rs b/src/optimize.rs index 7503c592..ab4147c3 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -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 } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index d8e256cc..f40f1d11 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -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(x: T, y: T) -> FuncReturn { 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(x: T, y: T) -> FuncReturn { 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(x: T, y: T) -> FuncReturn { 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(x: T) -> FuncReturn { 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(x: T) -> FuncReturn { 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(x: T, y: T) -> FuncReturn<::Output> { pub fn shl(x: T, y: INT) -> FuncReturn { // 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(x: T, y: INT) -> FuncReturn { // 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>(x: T, y: T) -> FuncReturn<>::Output> { // Checked modulo pub fn modulo(x: T, y: T) -> FuncReturn { 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(x: T, y: T) -> FuncReturn<::Output> { pub fn pow_i_i(x: INT, y: INT) -> FuncReturn { 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 { pub fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { // 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)) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 5574a4f5..fc6ca5ec 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -43,12 +43,13 @@ fn pad( && 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 { diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index f8fd2d33..0818e8fe 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -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) diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 149823c4..0b58c19d 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -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(); } } } diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index d554b68e..34f6821d 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -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) diff --git a/src/parser.rs b/src/parser.rs index 272215ac..8b3bf3ea 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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, 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)>, diff --git a/src/result.rs b/src/result.rs index cecd14bd..40f2935c 100644 --- a/src/result.rs +++ b/src/result.rs @@ -361,3 +361,9 @@ impl EvalAltResult { self } } + +impl From for Result> { + fn from(err: EvalAltResult) -> Self { + Err(err.into()) + } +} diff --git a/src/serde/de.rs b/src/serde/de.rs index f6d328db..6b0b8a53 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -53,11 +53,12 @@ impl<'de> DynamicDeserializer<'de> { } /// Shortcut for a type conversion error. fn type_error_str(&self, error: &str) -> Result> { - Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( error.into(), self.value.type_name().into(), Position::none(), - ))) + ) + .into() } fn deserialize_int>( &mut self, @@ -134,10 +135,8 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>( impl Error for Box { fn custom(err: T) -> Self { - Box::new(EvalAltResult::ErrorParsing( - ParseErrorType::BadInput(err.to_string()), - Position::none(), - )) + EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none()) + .into() } } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index c11c7f1c..f1a2ab77 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -85,9 +85,9 @@ impl DynamicSerializer { /// assert!(value.is::()); /// /// let map = value.cast::(); -/// let point = map.get("d").unwrap().downcast_ref::().unwrap(); -/// assert_eq!(*point.get("x").unwrap().downcast_ref::().unwrap(), 123.456); -/// assert_eq!(*point.get("y").unwrap().downcast_ref::().unwrap(), 999.0); +/// let point = map["d"].read_lock::().unwrap(); +/// assert_eq!(*point["x"].read_lock::().unwrap(), 123.456); +/// assert_eq!(*point["y"].read_lock::().unwrap(), 999.0); /// # } /// # Ok(()) /// # } @@ -99,10 +99,7 @@ pub fn to_dynamic(value: T) -> Result> impl Error for Box { fn custom(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) -> Result> { #[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> { @@ -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::().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::().unwrap(); diff --git a/src/serde/str.rs b/src/serde/str.rs index 7fb1c9cc..8283cc79 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -20,11 +20,12 @@ impl<'a> ImmutableStringDeserializer<'a> { } /// Shortcut for a type conversion error. fn type_error(&self) -> Result> { - Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + EvalAltResult::ErrorMismatchOutputType( type_name::().into(), "string".into(), Position::none(), - ))) + ) + .into() } } diff --git a/src/settings.rs b/src/settings.rs index d4949a41..6e9e020d 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -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"))] diff --git a/src/token.rs b/src/token.rs index 4a4d5fc1..c1968d23 100644 --- a/src/token.rs +++ b/src/token.rs @@ -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 { 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 { diff --git a/tests/closures.rs b/tests/closures.rs index 0736654b..3fc1cbd5 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -83,6 +83,7 @@ fn test_closures() -> Result<(), Box> { #[test] #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_object"))] +#[cfg(not(feature = "sync"))] fn test_closures_data_race() -> Result<(), Box> { let engine = Engine::new(); diff --git a/tests/side_effects.rs b/tests/side_effects.rs index bee1cb2f..69e33de1 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -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> { // Register type. engine.register_type_with_name::("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); diff --git a/tests/stack.rs b/tests/stack.rs index 29828fef..47202b47 100644 --- a/tests/stack.rs +++ b/tests/stack.rs @@ -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> { let engine = Engine::new(); assert_eq!( - engine.eval::( + engine.eval::( r" fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } } foo(8)