Skip to main content

Boa release v0.13

· 5 min read

Boa v0.13 is here! Boa is a JavaScript engine written in the Rust programming language. It makes it easy to embed a JS engine in your projects, and you can even use it from webassembly. See the about page for more info.

We currently support part of the language. In this release, our conformance has grown to 41.97% of the official ECMAScript Test Suite (Test262). We have closed 40 issues and merged 105 pull requests. You can check the full list of changes here.

This release brings some new features, such as support for calling Rust closures from JavaScript to improve better interopability between JS and Rust.

ECMAScript language features

named capture groups are now implemented and enabled.

const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;

const matchObj = RE_DATE.exec("1999-12-31");
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

This release brings support for the relative indexing method String.prototype.at(). This makes it easier to fetch values at the end of an array or string instead of doing str[str.length - 1].

const sentence = "The quick brown fox jumps over the lazy dog.";
let index = 5;
console.log(
`Using an index of ${index} the character returned is ${sentence.at(index)}`,
);
// expected output: "Using an index of 5 the character returned is u"

Other implemented language features include destructoring assignments, replaceAll(), Object.values(), Object.keys(), Object.preventExtensions(), splice(), sort(), spreading objects and more.

Boa API

Calling Rust closures from JavaScript

In addition to registering Rust functions as global JavaScript functions, our API has been expanded to register Rust closures. register_global_closure allows to capture variables in closures that can be called from javascript:

let mut context = Context::new();
let add_value = 1.0;
context.register_global_closure("addOne", 1, move |_, args, _| {
let argument = args.get(0).unwrap().as_number().unwrap();
Ok(JsValue::new(argument + add_value))
})?;
assert_eq!(context.eval("addOne(41)")?, 42.into());

To see the full capabilities of this feature, take a look at our examples. Thanks to @HalidOdat and @jedel1043 for their work on this.

Boa prelude

This release exposes a prelude of useful things that are already imported. This should make it more ergonomic to work with.

use boa::prelude::*; // This would import all the commonly-used things

fn main() {
let number = JSValue::number(3.1415);
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let result = forward(&mut engine, "Math.PI");
}

Test 262

Test262 is the implementation conformance test suite maintained by TC39. It's used by nearly all engines to measure how conformant they are to the specification. Boa pulls the tests in-tree and runs them against all PRs.

Since v0.12 we have managed to pass 6391 more tests and are 7% more conformant. This has been great progress by all involved and we hope this continues to improve. Most of these new passes have come from refactors across the codebase which have had little impact on performance, in fact, v0.13 is much faster than v0.12.

You can track Boa's conformance to the specification here

Keeping up with Rust

Rust is changing underneath us. This means we need to make sure our code is conforming the latest standards and we're taking advantage of the best optimizations the language can provide. In order to do this we rely on Clippy, this is Rust's in-house linter. We updated our code to respect the latest Rust version and updated formatting. Often a nice bonus of doing this is getting performance improvements for free as Clippy gets more "smarter". Thanks to @neeldug and @RageKnify for the work in this area.

Keeping things in order

Previously object properties were stored using FxHashMap. Despite being very fast it didn't offer any guarantees about the order. Properties need to retain the same order they were entered. In order to achieve this we switched over to IndexMap. Traits in Rust make this easy as IndexMap was designed to be a drop-in replacement for other HashMap implementations by following the same Trait. Thanks to IndexMap offering the possibility to use alternative hashing algorithms, we could continue using the fast FxHasher algorithm from rustc. Performance losses were minimal on some benchmarks and we actually made some gains in others. Thanks to @raskad for their work on this

VM

There is still on-going work to utilise a VM, this is happening alongside how Boa runs today. Although its not exposed yet there has been plenty of refactorings to the VM in order to make it performant. Hopefully we can talk about this in more detail soon.

Opening up the discussion

As of this release Boa has begun to utilise Github's Discussions feature. You can catch more long-ranging projects here

Thank You

This has been the biggest release yet, there have been many features and fixes. We want to thank all the contributors in this release, whether it was features, fixes or raising bugs.

If you're interested in contributing to Boa, we have some "good first issues" and "issues where help is wanted".