diff --git a/CHANGELOG.md b/CHANGELOG.md index 01b54a0e..ba1ac2a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,9 +30,9 @@ New features * `if`-expressions are allowed in `Engine::eval_expression` and `Engine::compile_expression` provided that both statement blocks each contain at most a single expression. * `switch`-expressions are allowed in `Engine::eval_expression` and `Engine::compile_expression` provided that match actions are expressions only. -### `fast_ops` feature +### Fast operators -* A new feature `fast_ops` is introduced that short-circuits all built-in operators of built-in types for higher speed. New user overloads are ignored. +* A new option `Engine::fast_operators` is introduced that short-circuits all built-in operators of built-in types for higher speed. User overloads are ignored. For operator-heavy scripts, this may yield substantial speed-up's. Enhancements ------------ diff --git a/Cargo.toml b/Cargo.toml index effb4b48..54331e51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,10 +39,9 @@ serde_bytes = "0.11" serde_json = { version = "1.0", default-features = false, features = ["alloc"] } [features] -default = ["std", "fast_ops"] +default = ["std"] std = ["ahash/std", "ahash/runtime-rng", "num-traits/std", "smartstring/std"] unchecked = [] # unchecked arithmetic -fast_ops = [] # ignore overloaded standard operators sync = [] # restrict to only types that implement Send + Sync no_position = [] # do not track position in the parser no_optimize = [] # no script optimizer diff --git a/src/api/options.rs b/src/api/options.rs index edbd222c..27be8635 100644 --- a/src/api/options.rs +++ b/src/api/options.rs @@ -7,26 +7,28 @@ use std::prelude::v1::*; bitflags! { /// Bit-flags containing all language options for the [`Engine`]. - pub struct LangOptions: u8 { + pub struct LangOptions: u16 { /// Is `if`-expression allowed? - const IF_EXPR = 0b_0000_0001; + const IF_EXPR = 0b_0000_0000_0001; /// Is `switch` expression allowed? - const SWITCH_EXPR = 0b_0000_0010; + const SWITCH_EXPR = 0b_0000_0000_0010; /// Is statement-expression allowed? - const STMT_EXPR = 0b_0000_0100; + const STMT_EXPR = 0b_0000_0000_0100; /// Is anonymous function allowed? #[cfg(not(feature = "no_function"))] - const ANON_FN = 0b_0000_1000; + const ANON_FN = 0b_0000_0000_1000; /// Is looping allowed? - const LOOPING = 0b_0001_0000; + const LOOPING = 0b_0000_0001_0000; /// Is variables shadowing allowed? - const SHADOW = 0b_0010_0000; + const SHADOW = 0b_0000_0010_0000; /// Strict variables mode? - const STRICT_VAR = 0b_0100_0000; + const STRICT_VAR = 0b_0000_0100_0000; /// Raise error if an object map property does not exist? /// Returns `()` if `false`. #[cfg(not(feature = "no_object"))] - const FAIL_ON_INVALID_MAP_PROPERTY = 0b_1000_0000; + const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0000_1000_0000; + /// Fast operators mode? + const FAST_OPS = 0b_0001_0000_0000; } } @@ -158,4 +160,16 @@ impl Engine { self.options .set(LangOptions::FAIL_ON_INVALID_MAP_PROPERTY, enable); } + /// Is fast operators mode enabled? + /// Default is `false`. + #[inline(always)] + #[must_use] + pub const fn fast_operators(&self) -> bool { + self.options.contains(LangOptions::FAST_OPS) + } + /// Set whether fast operators mode is enabled. + #[inline(always)] + pub fn set_fast_operators(&mut self, enable: bool) { + self.options.set(LangOptions::FAST_OPS, enable); + } } diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 09231462..661534a7 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -228,8 +228,7 @@ impl Engine { .. } = expr; - #[cfg(feature = "fast_ops")] - if *std_ops { + if *std_ops && self.fast_operators() { let mut lhs = self .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? .0 diff --git a/src/optimizer.rs b/src/optimizer.rs index 9539b372..0b7a13c2 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1181,7 +1181,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && (cfg!(feature = "fast_ops") || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { + _ if x.args.len() == 2 && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] diff --git a/tests/native.rs b/tests/native.rs index eed7b956..7081fe5f 100644 --- a/tests/native.rs +++ b/tests/native.rs @@ -77,19 +77,22 @@ fn test_native_overload() -> Result<(), Box> { assert_eq!( engine.eval::(r#"let x = "hello"; let y = "world"; x + y"#)?, - if cfg!(not(feature = "fast_ops")) { - "hello***world" - } else { - "helloworld" - } + "hello***world" ); assert_eq!( engine.eval::(r#"let x = "hello"; let y = (); x + y"#)?, - if cfg!(not(feature = "fast_ops")) { - "hello Foo!" - } else { - "hello" - } + "hello Foo!" + ); + + engine.set_fast_operators(true); + + assert_eq!( + engine.eval::(r#"let x = "hello"; let y = "world"; x + y"#)?, + "helloworld" + ); + assert_eq!( + engine.eval::(r#"let x = "hello"; let y = (); x + y"#)?, + "hello" ); Ok(()) diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 93a429ce..d231540b 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -49,24 +49,21 @@ fn test_optimizer_run() -> Result<(), Box> { run_test(&mut engine)?; // Override == operator - #[cfg(not(feature = "fast_ops"))] - { - engine.register_fn("==", |_x: INT, _y: INT| false); + engine.register_fn("==", |_x: INT, _y: INT| false); - engine.set_optimization_level(OptimizationLevel::Simple); + engine.set_optimization_level(OptimizationLevel::Simple); - assert_eq!( - engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, - 123 - ); + assert_eq!( + engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, + 123 + ); - engine.set_optimization_level(OptimizationLevel::Full); + engine.set_optimization_level(OptimizationLevel::Full); - assert_eq!( - engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, - 123 - ); - } + assert_eq!( + engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, + 123 + ); Ok(()) } diff --git a/tests/plugins.rs b/tests/plugins.rs index a7912a0a..be1cf49b 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -128,14 +128,7 @@ fn test_plugins_package() -> Result<(), Box> { assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; hi(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); - assert_eq!( - engine.eval::("2 + 2")?, - if cfg!(not(feature = "fast_ops")) { - 5 - } else { - 4 - } - ); + assert_eq!(engine.eval::("2 + 2")?, 5); assert_eq!( engine.eval::("let a = [1, 2, 3]; greet(test(a, 2))")?, "6 kitties"