rhai/RELEASES.md
2020-06-14 00:09:16 +08:00

8.4 KiB

Rhai Release Notes

Version 0.15.1

This is a minor release which enables updating indexers (via registered indexer setters) and supports functions with &str parameters (maps transparently to ImmutableString).

Breaking changes

  • Callback closure passed to Engine::on_progress now takes &u64 instead of u64 to be consistent with other callback signatures.
  • Engine::register_indexer is renamed to Engine::register_indexer_get.
  • Module::set_indexer_fn is renamed to Module::set_indexer_get_fn.
  • The tuple ParseError now exposes the internal fields and the ParseError::error_type and ParseError::position methods are removed. The first tuple field is the ParseErrorType and the second tuple field is the Position.
  • Engine::call_fn_dynamic now takes any type that implements IntoIterator<Item = Dynamic>.

New features

  • Indexers are now split into getters and setters (which now support updates). The API is split into Engine::register_indexer_get and Engine::register_indexer_set with Engine::register_indexer_get_set being a shorthand. Similarly, Module::set_indexer_get_fn and Module::set_indexer_set_fn are added.
  • Engine:register_fn and Engine:register_result_fn accepts functions that take parameters of type &str (immutable string slice), which maps directly to ImmutableString. This is to avoid needing wrappers for functions taking string parameters.
  • Set maximum limit on data sizes: Engine::set_max_string_size, Engine::set_max_array_size and Engine::set_max_map_size.

Version 0.15.0

This version uses immutable strings (ImmutableString type) and built-in operator functions (e.g. +, >, +=) to improve speed, plus some bug fixes.

Regression fix

  • Do not optimize script with eval_expression - it is assumed to be one-off and short.

Bug fixes

  • Indexing with an index or dot expression now works property (it compiled wrongly before). For example, let s = "hello"; s[s.len-1] = 'x'; now works property instead of causing a runtime error.
  • if expressions are not supposed to be allowed when compiling for expressions only. This is fixed.

Breaking changes

  • Engine::compile_XXX functions now return ParseError instead of Box<ParseError>.
  • The RegisterDynamicFn trait is merged into the RegisterResultFn trait which now always returns Result<Dynamic, Box<EvalAltResult>>.
  • Default maximum limit on levels of nested function calls is fine-tuned and set to a different value.
  • Some operator functions are now built in (see Speed enhancements below), so they are available even under Engine::new_raw.
  • Strings are now immutable. The type rhai::ImmutableString is used instead of std::string::String. This is to avoid excessive cloning of strings. All native-Rust functions taking string parameters should switch to rhai::ImmutableString (which is either Rc<String> or Arc<String> depending on whether the sync feature is used).
  • Native Rust functions registered with the Engine also mutates the first argument when called in normal function-call style (previously the first argument will be passed by value if not called in method-call style). Of course, if the first argument is a calculated value (e.g. result of an expression), then mutating it has no effect, but at least it is not cloned.
  • Some built-in methods (e.g. len for string, floor for FLOAT) now have property versions in addition to methods to simplify coding.

New features

  • Set limit on maximum level of nesting expressions and statements to avoid panics during parsing.
  • New EvalPackage to disable eval.
  • Module::set_getter_fn, Module::set_setter_fn and Module:set_indexer_fn to register getter/setter/indexer functions.
  • Engine::call_fn_dynamic for more control in calling script functions.

Speed enhancements

  • Common operators (e.g. +, >, ==) now call into highly efficient built-in implementations for standard types (i.e. INT, FLOAT, bool, char, () and ImmutableString) if not overridden by a registered function. This yields a 5-10% speed benefit depending on script operator usage. Scripts running tight loops will see significant speed-up.
  • Common assignment operators (e.g. +=, %=) now call into highly efficient built-in implementations for standard types (i.e. INT, FLOAT, bool, char, () and ImmutableString) if not overridden by a registered function.
  • Implementations of common operators for standard types are removed from the ArithmeticPackage and LogicPackage (and therefore the CorePackage) because they are now always available, even under Engine::new_raw.
  • Operator-assignment statements (e.g. +=) are now handled directly and much faster.
  • Strings are now immutable and use the rhai::ImmutableString type, eliminating large amounts of cloning.
  • For Native Rust functions taking a first &mut parameter, the first argument is passed by reference instead of by value, even if not called in method-call style. This allows many functions declared with &mut parameter to avoid excessive cloning. For example, if a is a large array, getting its length in this manner: len(a) used to result in a full clone of a before taking the length and throwing the copy away. Now, a is simply passed by reference, avoiding the cloning altogether.
  • A custom hasher simply passes through u64 keys without hashing to avoid function call hash keys (which are by themselves u64) being hashed twice.

Version 0.14.1

The major features for this release is modules, script resource limits, and speed improvements (mainly due to avoiding allocations).

New features

  • Modules and module resolvers allow loading external scripts under a module namespace. A module can contain constant variables, Rust functions and Rhai functions.
  • export variables and private functions.
  • Indexers for Rust types.
  • Track script evaluation progress and terminate script run.
  • Set limit on maximum number of operations allowed per script run.
  • Set limit on maximum number of modules loaded per script run.
  • A new API, Engine::compile_scripts_with_scope, can compile a list of script segments without needing to first concatenate them together into one large string.
  • Stepped range function with a custom step.

Speed improvements

StaticVec

A script contains many lists - statements in a block, arguments to a function call etc. In a typical script, most of these lists tend to be short - e.g. the vast majority of function calls contain fewer than 4 arguments, while most statement blocks have fewer than 4-5 statements, with one or two being the most common. Before, dynamic Vec's are used to hold these short lists for very brief periods of time, causing allocations churn.

In this version, large amounts of allocations are avoided by converting to a StaticVec - a list type based on a static array for a small number of items (currently four) - wherever possible plus other tricks. Most real-life scripts should see material speed increases.

Pre-computed variable lookups

Almost all variable lookups, as well as lookups in loaded modules, are now pre-computed. A variable's name is almost never used to search for the variable in the current scope.

Getters and setter function names are also pre-computed and cached, so no string allocations are performed during a property get/set call.

Pre-computed function call hashes

Lookup of all function calls, including Rust and Rhai ones, are now through pre-computed hashes. The function name is no longer used to search for a function, making function call dispatches much faster.

Large Boxes for expressions and statements

The expression (Expr) and statement (Stmt) types are modified so that all of the variants contain only one single Box to a large allocated structure containing all the fields. This makes the Expr and Stmt types very small (only one single pointer) and improves evaluation speed due to cache efficiency.

Error handling

Previously, when an error occurs inside a function call, the error position reported is the function call site. This makes it difficult to diagnose the actual location of the error within the function.

A new error variant EvalAltResult::ErrorInFunctionCall is added in this version. It wraps the internal error returned by the called function, including the error position within the function.