Releases: rhaiscript/rhai
v1.16.2
v1.16.1
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_sizeetc.) as well asEngine::ensure_data_size_within_limitsare no longer exported underunchecked. This should be the correct behavior instead of returningNoneor zero. - The type
OptimizationLevelis 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
thisas 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). maxandminfor 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.tomlto avoid breaking changes in future versions. bitflagsis bumped to version 2.syninrhai_codegenis bumped to version 2.hashbrown(used inno-stdbuilds) is bumped to version 0.14.
Deprecated API's
ParseErrorType::MalformedCallExprandParseErrorType::MalformedInExprare deprecated and will be removed in the next major version.Module::get_custom_typeis deprecated in favor ofModule::get_custom_type_display_by_nameand other new methods.
New features
- New
exitfunction that terminates script evaluation regardless of where it is called, even inside deeply-nested function calls. - Added
Engine::max_variablesandEngine::set_max_variablesto 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_variablesis exceeded a new error,ErrorTooManyVariables, is returned. - Added
zipfunction for arrays. - Added
on_printandon_debugdefinitions 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_cellis used instdenvironments instead of the home-brewSusLockwhich 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
Vecbecause they are not transient and do not appear to improve performance. UsingVecseems 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
thisshould now run slightly faster due to a dedicatedASTnodeThisPtr. - A
takefunction is added to the standard library to take ownership of any data (replacing with()) in order to avoid cloning. Dynamic::takeis added to take ownership of the data (replacing with()) in order to avoid cloning.EvalAltResult::ErrorMismatchOutputTypenow gives a better name for the requested generic type (e.g.&stris now&strand 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_sharedis 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_astwith constants now works correctly for closures. Previously the hiddenSharenodes 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()).propnow 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; Xnow 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-eabitarget (e.g. Raspberry Pi Pico) now completes successfully. Dependency tono-std-compatis now pointed to the latest repo instead ofcrates.io.
New features
- It is now possible to require a specific type to the
thispointer for a particular script-defined function so that it is called only when thethispointer contains the specified type. is_def_fnis extended to support checking for typed methods, with syntaxis_def_fn(this_type, fn_name, arity)Dynamic::takeis added as a short-cut forstd::mem::take(&mut value).
Enhancements
Engine::is_symbol_disabledis added to test whether a particular keyword/symbol is disabled.- Support is added to deserialize a
Dynamicvalue 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 mapandfilterfor arrays are markedpure. Warnings are added to the documentation of pure array methods that takethisclosures.- Syntax such as
foo.bar::bazno longer panics, but returns a proper parse error. - Expressions such as
!insidenow parses correctly instead of as!infollowed 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 += ywherexandyarechar, 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 Scopeparameter. 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 Scopeinstead of theScopeparameter. This is a breaking change because the&mutis now required.Engine::allow_loop_expressionsnow correctly defaults totrue(was erroneouslyfalseby default).
Enhancements
Engine::new_rawis nowconstand runs very fast, delaying all other initialization until first use.- The functions
minandmaxare added for numbers. - Range cases in
switchstatements 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
importnow 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
isAnonymousis added to JSON functions metadata.
v1.12.0
Bug fixes
- Integer numbers that are too large to deserialize into
INTnow fall back toDecimalorFLOATinstead 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_expressionetc. - Strings interpolation now works under
Engine::new_rawwithout any standard package. Fnnow 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_capacityis deprecated.- The internal method
Engine::eval_statements_rawis 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
exported from modules!
!in
- A new operator
!inis added which maps to!(... in ...).
Engine::call_fn_with_options
Engine::call_fn_rawis deprecated in favor ofEngine::call_fn_with_optionswhich 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_scriptis 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_scriptdoes 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,reduceetc.), can now bind the array element tothiswhen calling a closure. - This vastly improves performance when working with arrays of large types (e.g. object maps) by avoiding unnecessary cloning.
findandfind_mapare added for arrays.for_eachis 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. CallableFunctionis exported underinternals.- The
TypeBuildertype andCustomTypetrait are no longer marked as volatile. FuncArgsis also implemented for arrays.Engine::set_XXXAPI can now be chained.EvalContext::scope_mutnow returns&mut Scopeinstead 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
docfield. - Expression nesting levels is refined such that it grows less excessively for common patterns.
- The traits
IndexandIndexMutare added toFnPtr. FnPtr::iter_curryandFnPtr::iter_curry_mutare added.Dynamic::deep_scanis added to recursively scan forDynamicvalues.>>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_jsonnow returns an error on unquoted keys to be consistent with JSON specifications.importstatements insideevalno longer cause errors in subsequent code.- Functions marked
globalinimported modules with no alias names now work properly. - Incorrect loop optimizations that are too aggressive (e.g. unrolling a
do { ... } until truewith abreakstatement inside) and cause crashes are removed. Dynamic::isnow works properly for shared values.
Breaking changes
NativeCallContext::newis completely deprecated and unimplemented (always panics) in favor of new API's.
New features
Dynamic detection API
- New methods are added to
Dynamicin the form ofis_XXX()whereXXXis 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,whileandfor) can now act as expressions, with thebreakstatement 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
ahashhasher, via a static functionrhai::config::hashing::set_ahash_seedor 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
Scopeis now serializable and deserializable viaserde.
Store and recreate NativeCallContext
- A convenient API is added to store a
NativeCallContextinto a newNativeCallContextStoretype. - This allows a
NativeCallContextto be stored and recreated later on.
Call native Rust functions in NativeCallContext
NativeCallContext::call_native_fnis added to call registered native Rust functions only.NativeCallContext::call_native_fn_rawis 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_jsonfunction is added to parse a JSON string into an object map.Error::ErrorNonPureMethodCallOnConstantis 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
decimalfeature.
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.