Releases: rhaiscript/rhai
v1.16.0
This version is a general code cleanup such that it relies less on obscure hacks unless those tricks provide genuine performance benefits. It should make the code base easier to follow for new contributors.
The minimum Rust compiler version is raised to 1.66.0
.
Potentially-breaking changes
- Limit functions (e.g.
max_operations
,max_array_size
etc.) as well asEngine::ensure_data_size_within_limits
are no longer exported underunchecked
. This should be the correct behavior instead of returningNone
or zero. - The type
OptimizationLevel
is no longer exported underno_optimize
. Originally it was mapped to()
underno_optimize
. - O/S features such as file access and time are no longer disabled when using
wasm32-wasi
(or any WASM target other thanwasm32-unknown
).
Bug fixes
- Fixes a panic when using
this
as the first parameter in a namespace-qualified function call. - Comparing two different data types (e.g. a custom type and a standard type) now correctly defaults to
false
(except for!=
which defaults totrue
). max
andmin
for integers, strings and characters were missing from the standard library. They are now added.
Dependencies
- Minimal version numbers for dependencies are now specified in
Cargo.toml
to avoid breaking changes in future versions. bitflags
is bumped to version 2.syn
inrhai_codegen
is bumped to version 2.hashbrown
(used inno-std
builds) is bumped to version 0.14.
Deprecated API's
ParseErrorType::MalformedCallExpr
andParseErrorType::MalformedInExpr
are deprecated and will be removed in the next major version.Module::get_custom_type
is deprecated in favor ofModule::get_custom_type_display_by_name
and other new methods.
New features
- New
exit
function that terminates script evaluation regardless of where it is called, even inside deeply-nested function calls. - Added
Engine::max_variables
andEngine::set_max_variables
to limit the maximum number of variables allowed within a scope at any time. This is to guard against defining a huge number of variables containing large data just beyond individual data size limits. Whenmax_variables
is exceeded a new error,ErrorTooManyVariables
, is returned. - Added
zip
function for arrays. - Added
on_print
andon_debug
definitions forTypeBuilder
. - Doc-comments are now included in custom type definitions within plugin modules. They can be accessed via
Module::get_custom_type_raw
. These doc-comments for custom types are also exported in JSON viaEngine::gen_fn_metadata_to_json
.
Enhancements
once_cell
is used instd
environments instead of the home-brewSusLock
which is removed.- Originally, unit tests use the
?
operator liberally to simplify code. However, this causes the loss of proper line numbers when a test fails, making it difficult to identify the exact location of the failure. This is now fixed by changing tounwrap()
. - Many inlined collections are turned back into
Vec
because they are not transient and do not appear to improve performance. UsingVec
seems to be yield better performance as it probably enables more compiler optimizations. - General code clean-up to remove optimizations tricks that are not obviously beneficial in favor of clearer code.
v1.15.1
v1.15.0
Bug fixes
- Fixes a concurrency error in static hashing keys (thanks
garypen
!).
Enhancements
- Expressions involving
this
should now run slightly faster due to a dedicatedAST
nodeThisPtr
. - A
take
function is added to the standard library to take ownership of any data (replacing with()
) in order to avoid cloning. Dynamic::take
is added to take ownership of the data (replacing with()
) in order to avoid cloning.EvalAltResult::ErrorMismatchOutputType
now gives a better name for the requested generic type (e.g.&str
is now&str
and notstring
).
v1.14.0
This new version contains a substantial number of bug fixes for edge cases.
A new syntax is supported to facilitate writing object methods in script.
The code hacks that attempt to optimize branch prediction performance are removed because benchmarks do not show any material speed improvements.
Bug fixes
is_shared
is a reserved keyword and is now handled properly (e.g. it cannot be the target of a function pointer).- Re-optimizing an AST via
optimize_ast
with constants now works correctly for closures. Previously the hiddenShare
nodes are not removed and causes variable-not-found errors during runtime if the constants are not available in the scope. - Expressions such as
(v[0].func()).prop
now parse correctly. - Shadowed variable exports are now handled correctly.
- Shadowed constant definitions are now optimized correctly when propagated (e.g.
const X = 1; const X = 1 + 1 + 1; X
now evaluates to 3 instead of 0). - Identifiers and comma's in the middle of custom syntax now register correctly.
- Exporting an object map from a module with closures defined on properties now works correctly when those properties are called to mimic method calls in OOP-style.
- Compiling for
thumbv6m-none-eabi
target (e.g. Raspberry Pi Pico) now completes successfully. Dependency tono-std-compat
is now pointed to the latest repo instead ofcrates.io
.
New features
- It is now possible to require a specific type to the
this
pointer for a particular script-defined function so that it is called only when thethis
pointer contains the specified type. is_def_fn
is extended to support checking for typed methods, with syntaxis_def_fn(this_type, fn_name, arity)
Dynamic::take
is added as a short-cut forstd::mem::take(&mut value)
.
Enhancements
Engine::is_symbol_disabled
is added to test whether a particular keyword/symbol is disabled.- Support is added to deserialize a
Dynamic
value containing custom types or shared values back into anotherDynamic
(essentially a straight cloned copy).
v1.13.0
This version attempts a number of optimizations that may yield small speed improvements:
- Simple operators (e.g. integer arithmetic) are inlined to avoid the overhead of a function call.
- The tokenizer uses pre-calculated tables (generated by GNU
gperf
) for keyword recognition. - A black-arts trick (see
Engine::black_box
) is used to prevent LLVM from optimizing hand-tuned AST node matches back into a lookup table, which messes up branch prediction on modern CPU's.
Bug fixes
- Complex indexing/dotting chains now parse correctly, for example:
a[b][c[d]].e
map
andfilter
for arrays are markedpure
. Warnings are added to the documentation of pure array methods that takethis
closures.- Syntax such as
foo.bar::baz
no longer panics, but returns a proper parse error. - Expressions such as
!inside
now parses correctly instead of as!in
followed byside
. - Custom syntax starting with symbols now works correctly and no longer raises a parse error.
- Comparing different custom types now works correctly when the appropriate comparison operators are registered.
- Some op-assignments, such as
x += y
wherex
andy
arechar
, now work correctly instead of failing silently. - Op-assignments to bit flags or bit ranges now work correctly.
Potentially breaking changes
- The trait method
ModuleResolver::resolve_raw
(which is a low-level API) now takes a&mut Scope
parameter. This is a breaking change because the signature is modified, but this trait method has a default and is rarely called/implemented in practice. Module::eval_ast_as_new_raw
(a low-level API) now takes a&mut Scope
instead of theScope
parameter. This is a breaking change because the&mut
is now required.Engine::allow_loop_expressions
now correctly defaults totrue
(was erroneouslyfalse
by default).
Enhancements
Engine::new_raw
is nowconst
and runs very fast, delaying all other initialization until first use.- The functions
min
andmax
are added for numbers. - Range cases in
switch
statements now also match floating-point and decimal values. In order to support this, however, small numeric ranges cases are no longer unrolled. - Loading a module via
import
now gives the module access to the current scope, including variables and constants defined inside. - Some very simple operator calls (e.g. integer add) are inlined to avoid the overhead of a function call, resulting in a small speed improvement.
- The tokenizer now uses table-driven keyword recognizers generated by GNU
gperf
. At least theoretically it should be faster... - The field
isAnonymous
is added to JSON functions metadata.
v1.12.0
Bug fixes
- Integer numbers that are too large to deserialize into
INT
now fall back toDecimal
orFLOAT
instead of silently truncating. - Parsing deeply-nested closures (e.g.
||{||{||{||{||{||{||{...}}}}}}}
) no longer panics but will be confined to the nesting limit. - Closures containing a single expression are now allowed in
Engine::eval_expression
etc. - Strings interpolation now works under
Engine::new_raw
without any standard package. Fn
now throws an error if the name is a reserved keyword as it cannot possibly map to such a function. This also disallows creating function pointers to custom operators which are defined as disabled keywords (a mouthful), but such custom operators are designed primarily to be used as operators.
Breaking API changes
- The callback for initializing a debugger instance has changed to
Fn(&Engine, Debugger) -> Debugger
. This allows more control over the initial setup of the debugger. - The internal macro
reify!
is no longer available publicly.
Deprecated API's
Module::with_capacity
is deprecated.- The internal method
Engine::eval_statements_raw
is deprecated. - Array overloaded methods that take function names (as string) are deprecated in favor of using the
Fn("...")
call.
Speed improvements
- The function registration mechanism is revamped to take advantage of constant generics, among others, to omit checking code where possible. This yields a 10-20% speed improvements on certain real-life, function-call-heavy workloads.
- Functions taking function pointers as parameters, usually called with closures, now run faster because a link to the anonymous function (generated by the closure) is stored together with the function pointer itself. This allows short-circuiting the function lookup step.
Net features
First class functions (sort of)
- A function pointer created via a closure definition now links to the particular anonymous function itself.
- This avoids a potentially expensive function lookup when the function pointer is called, speeding up closures.
- Closures now also encapsulate their defining environment, so function pointers can now be freely
export
ed from modules!
!in
- A new operator
!in
is added which maps to!(... in ...)
.
Engine::call_fn_with_options
Engine::call_fn_raw
is deprecated in favor ofEngine::call_fn_with_options
which allows setting options for the function call.- The options are for future-proofing the API.
- In this version, it gains the ability to set the value of the custom state (accessible via
NativeCallContext::tag
) for a function evaluation, overridingEngine::set_default_tag
.
Compact a script for compression
Engine::compact_script
is added which takes a valid script (it still returns parsing errors) and returns a compacted version of the script with all insignificant whitespaces and all comments removed.- A compact script compresses better than one with liberal whitespaces and comments.
- Unlike some uglifiers or minifiers,
Engine::compact_script
does not optimize the script in any way, nor does it rename variables.
Enhanced array API
- Array methods that take a function pointer, usually a closure (e.g.
map
,filter
,index_of
,reduce
etc.), can now bind the array element tothis
when calling a closure. - This vastly improves performance when working with arrays of large types (e.g. object maps) by avoiding unnecessary cloning.
find
andfind_map
are added for arrays.for_each
is also added for arrays, allowing a closure to mutate array elements (bound tothis
) in turn.
Enhancements
- Optimizations have been done to key data structures to minimize size and creation time, which involves turning rarely-used fields into
Option<Box<T>>
. This resulted in some speed improvements. CallableFunction
is exported underinternals
.- The
TypeBuilder
type andCustomType
trait are no longer marked as volatile. FuncArgs
is also implemented for arrays.Engine::set_XXX
API can now be chained.EvalContext::scope_mut
now returns&mut Scope
instead of&mut &mut Scope
.- Line-style doc-comments are now merged into a single string to avoid creating many strings. Block-style doc-comments continue to be independent strings.
- Block-style doc-comments are now "un-indented" for better formatting.
- Doc-comments on plugin modules are now captured in the module's
doc
field. - Expression nesting levels is refined such that it grows less excessively for common patterns.
- The traits
Index
andIndexMut
are added toFnPtr
. FnPtr::iter_curry
andFnPtr::iter_curry_mut
are added.Dynamic::deep_scan
is added to recursively scan forDynamic
values.>>
and<<
operators on integers no longer throw errors when the number of bits to shift is out of bounds. Shifting by a negative number of bits simply reverses the shift direction.
v1.11.0
This is a large release containing numerous new features, bug fixes and speed improvements.
Speed Improvements
- Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
Bug fixes
Engine::parse_json
now returns an error on unquoted keys to be consistent with JSON specifications.import
statements insideeval
no longer cause errors in subsequent code.- Functions marked
global
inimport
ed modules with no alias names now work properly. - Incorrect loop optimizations that are too aggressive (e.g. unrolling a
do { ... } until true
with abreak
statement inside) and cause crashes are removed. Dynamic::is
now works properly for shared values.
Breaking changes
NativeCallContext::new
is completely deprecated and unimplemented (always panics) in favor of new API's.
New features
Dynamic
detection API
- New methods are added to
Dynamic
in the form ofis_XXX()
whereXXX
is a type (e.g.is_int
,is_unit
,is_bool
,is_array
). - This new API is to make it easier to detect the data type, instead of having to call
is::<XXX>()
.
Loop expressions
- Loops (such as
loop
,do
,while
andfor
) can now act as expressions, with thebreak
statement returning an optional value. - Normal loops return
()
as the value. - Loop expressions can be enabled/disabled via
Engine::set_allow_loop_expressions
Static hashing
- It is now possible to specify a fixed seed for use with the
ahash
hasher, via a static functionrhai::config::hashing::set_ahash_seed
or an environment variable (RHAI_AHASH_SEED
), in order to force static (i.e. deterministic) hashes for function signatures. - This is necessary when using Rhai across shared-library boundaries.
- A build script is used to extract the environment variable (
RHAI_AHASH_SEED
, if any) and splice it into the source code before compilation.
no_time
for no timestamps
- A new feature,
no_time
, is added to disable support for timestamps. - This may be necessary when building for architectures without time support, such as raw WASM.
Serializable Scope
Scope
is now serializable and deserializable viaserde
.
Store and recreate NativeCallContext
- A convenient API is added to store a
NativeCallContext
into a newNativeCallContextStore
type. - This allows a
NativeCallContext
to be stored and recreated later on.
Call native Rust functions in NativeCallContext
NativeCallContext::call_native_fn
is added to call registered native Rust functions only.NativeCallContext::call_native_fn_raw
is added as the advanced version.- This is often desirable as Rust functions typically do not want a similar-named scripted function to hijack the process -- which will cause brittleness.
Custom syntax improvements
- The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term
string
). - This facilitates more accurate parsing by separating strings and identifiers.
Limits API
- Methods returning maximum limits (e.g.
Engine::max_string_len
) are now available even underunchecked
. - This helps avoid the proliferation of unnecessary feature flags in third-party library code.
Enhancements
parse_json
function is added to parse a JSON string into an object map.Error::ErrorNonPureMethodCallOnConstant
is added which is raised when a non-pure method is called on a constant value.
v1.10.1
This is a bug-fix release that fixes an error when compiling for 32-bit architectures.
Bug fixes
- Compiling on 32-bit architectures no longer cause a compilation error.
- Fix type-size test for 32-bit architectures without the
decimal
feature.
Custom syntax with state
- [
Engine::register_custom_syntax_with_state_raw
] is added. The custom syntax parser and implementation functions take on an additional parameter that holds a user-defined custom state which should substantially simplify writing some custom parsers. - [
Engine::register_custom_syntax_raw
] is deprecated.
v1.10.0
This version introduces Fast Operators mode, which is turned on by default but can be disabled via
a new options API: Engine::set_fast_operators
.
Fast Operators mode assumes that none of Rhai's built-in operators for standard data types are
overloaded by user-registered functions. In the vast majority of cases this should be so (really,
who overloads the +
operator for integers anyway?).
This assumption allows the Engine
to avoid checking for overloads for every single operator call.
This usually results in substantial speed improvements, especially for expressions.
Minimum Rust Version
The minimum Rust version is now 1.61.0
in order to use some const
generics.
Bug fixes
- API for registering property getters/setters and indexers to an
Engine
now works with functions that take a first parameter ofNativeCallContext
. - Missing API function
Module::set_getter_setter_fn
is added. - To avoid subtle errors, simple optimization is used for
rhai-run
; previous it was full optimization.
Deprecated API
- All versions of the
Engine::register_XXX_result
API that register a function returningResult<T, Box<EvalAltResult>>
are now deprecated. The regular, non-result
versions handle all functions correctly.
New features
Fast operators
- A new option
Engine::fast_operators
is introduced (default totrue
) to enable/disable Fast Operators mode.
Fallible type iterators
- For very special needs, the ability to register fallible type iterators is added.
Expressions
if
-expressions are allowed inEngine::eval_expression
andEngine::compile_expression
provided that both statement blocks each contain at most a single expression.switch
-expressions are allowed inEngine::eval_expression
andEngine::compile_expression
provided that match actions are expressions only.
Enhancements
is_empty
method is added to arrays, BLOB's, object maps, strings and ranges.StaticModuleResolver
now stores the path in the module'sid
field.Engine::module_resolver
is added to grant access to theEngine
's module resolver.- Constants and variables now have types in generated definition files.