@@ -14,7 +14,7 @@ Boa makes it easy to embed a JS engine in your projects, and you can
14
14
even use it from WebAssembly. See the [ about] ( /about ) page for more
15
15
info.
16
16
17
- In this release, our conformance has grown from 89.92% to 93.94 % in the
17
+ In this release, our conformance has grown from 89.92% to 94.12 % in the
18
18
official ECMAScript Test Suite (Test262). Our growth in conformance is
19
19
driven by increased conformance for Temporal (discussed further below)
20
20
with the rest of the development effort being focused on performance,
@@ -34,47 +34,159 @@ information on conformance [here][conformance].
34
34
35
35
There has been a lot of progress made on Temporal, the new Stage 3
36
36
date/time proposal. With this release, Boa's conformance on Temporal
37
- grew from 40.67% to ~ 97%.
37
+ grew from 40.67% to ~ 97%. This implementation is backed by the ` temporal_rs `
38
+ date/time Rust library, which we went over in our announcement
39
+ [ blog post] ( ../2025-09-24-temporal-release ) . Give the post a read if you are
40
+ interested in ` temporal_rs ` and its development history.
38
41
39
- The implementation is backed by the ` temporal_rs ` date/time Rust
40
- library, which we went over briefly in our June
41
- [ blog post] ( ../2025-06-15-temporal-impl-1.md ) with hopefully another post
42
- in the not too distant future. So far, ` temporal_rs ` has also been used in
43
- both V8 and Keisel to implement Temporal.
42
+ ### Span nodes and error backtraces
44
43
45
- Temporal can be used from ` boa_cli ` or enabled in ` boa_engine ` with the
46
- ` experimental ` or ` temporal ` feature.
44
+ We added support for storing spans in our AST nodes, which allows determining the
45
+ exact location of an AST node on its original file. We already kind of
46
+ supported this feature in our lexer, but we did not store the spans after parsing.
47
47
48
- You can also try out Temporal directly in Rust using ` temporal_rs ` :
48
+ Why is this important? Well, as a direct result from this, Boa now supports error backtraces
49
+ when an exception is thrown!
49
50
50
- ```
51
- cargo add temporal_rs
51
+ ![ backtrace-example] ( ./img/backtrace.gif )
52
+
53
+ As an additional plus, you can enable the ` native-backtrace ` feature to include
54
+ "native" functions on a backtrace.
55
+
56
+ ![ native-backtrace-example] ( ./img/native-backtrace.gif )
57
+
58
+ This feature has been one of the most requested ones for years,
59
+ and we hope it will
60
+ greatly help with debugging errors when using Boa.
61
+
62
+ ### Async APIs enhancements
63
+
64
+ Historically, hooking functions returning a ` Future ` into Boa has been one of the
65
+ biggest pain points of our API. This was mostly caused by how we defined
66
+ ` FutureJob ` :
67
+
68
+ ``` rust
69
+ pub type FutureJob = Pin <Box <dyn Future <Output = NativeJob > + 'static >>;
52
70
```
53
71
54
- #### Special thanks
72
+ With this definition, it was pretty much impossible to capture the ` Context `
73
+ inside the future, and functions that needed to interweave engine operations
74
+ with awaiting ` Future ` s would have to be split into multiple parts:
75
+
76
+ ``` rust
77
+ let fetch = async move {
78
+ let body : Result <_ , isahc :: Error > = async {
79
+ let mut response = Request :: get (& url )
80
+ . body (())?
81
+ . send_async ()
82
+ . await ? ;
83
+
84
+ Ok (response . text (). await ? )
85
+ }
86
+ . await ;
87
+
88
+ // Since the async context cannot take the `context` by ref, we have to continue
89
+ // parsing inside a new `NativeJob` that will be enqueued into the promise job queue.
90
+ NativeJob :: new (move | context | -> JsResult <JsValue > {
91
+ parse (body ). await ;
92
+
93
+ // Also needed to match `NativeJob::new`.
94
+ Ok (JsValue :: undefined ())
95
+ })
96
+ };
97
+
98
+ // Just enqueue the future for now. We'll advance all the enqueued futures inside our custom
99
+ // `JobQueue`.
100
+ context
101
+ . job_queue ()
102
+ . enqueue_future_job (Box :: pin (fetch ), context )
103
+ }
104
+ ```
55
105
56
- Special thanks to all the student's from University of Bergen who helped contribute to
57
- ` temporal_rs ` and Boa, as well as Shane Carr ([ @sffc ] ( https://github.com/@sffc ) ) and
58
- Manish Goregaokar ([ @manishearth ] ( https://github.com/@manishearth ) ) for their contributions
59
- and help with ` temporal_rs ` .
106
+ We wanted to improve this API, and the solution we thought about was to make
107
+ ` Context ` shareable by wrapping it using ` RefCell ` . However, this proved to be
108
+ very difficult for two reasons:
109
+ 1 . We needed to change all definitions to take ` &RefCell<Context> ` instead
110
+ of ` &mut Context ` , which meant changing pretty much the whole codebase.
111
+ 2 . Some of our VM code was reentrant, which meant calling ` RefCell::borrow_mut `
112
+ would cause panics in the reentrant parts of the code; we would need to
113
+ redesign some parts of the engine to remove the reentrancy.
114
+
115
+ After putting a lot of thought on this, we came up with a really nice solution;
116
+ instead of wrapping ` Context ` with ` RefCell ` , we would wrap ` &mut Context ` with
117
+ ` RefCell ` , and only on the async-related APIs. This would allow not only capturing
118
+ the context to ` Future ` -related functions, but also doing this without having to
119
+ refactor big parts of the code. Thus, we ditched ` FutureJob ` and introduced a new
120
+ type of job: ` NativeAsyncJob ` .
121
+
122
+ ``` Rust
123
+ /// An ECMAScript [Job] that can be run asynchronously.
124
+ ///
125
+ /// This is an additional type of job that is not defined by the specification, enabling running `Future` tasks
126
+ /// created by ECMAScript code in an easier way.
127
+ #[allow(clippy:: type_complexity)]
128
+ pub struct NativeAsyncJob {
129
+ f : Box <dyn for <'a > FnOnce (& 'a RefCell <& mut Context >) -> BoxedFuture <'a >>,
130
+ realm : Option <Realm >,
131
+ }
132
+ ```
60
133
61
- ### Span nodes and error backtraces
134
+ With this change, any API that integrates with ` Future ` can additionally capture
135
+ the ` &RefCell<&mut Context> ` to run engine-related operations after awaiting on
136
+ a ` Future ` .
137
+
138
+ ### Revamped ` JobQueue `
139
+
140
+ After introducing the new job type, changes had to be made on
141
+ [ ` JobQueue ` ] ( https://docs.rs/boa_engine/0.20.0/boa_engine/job/trait.JobQueue.html )
142
+ to better support new job types. Thus, ` JobQueue ` was revamped and renamed to be the
143
+ new ` JobExecutor ` :
144
+
145
+ ``` rust
146
+ /// An executor of `ECMAscript` [Jobs].
147
+ ///
148
+ /// This is the main API that allows creating custom event loops.
149
+ ///
150
+ /// [Jobs]: https://tc39.es/ecma262/#sec-jobs
151
+ pub trait JobExecutor : Any {
152
+ /// Enqueues a `Job` on the executor.
153
+ ///
154
+ /// This method combines all the host-defined job enqueueing operations into a single method.
155
+ /// See the [spec] for more information on the requirements that each operation must follow.
156
+ ///
157
+ /// [spec]: https://tc39.es/ecma262/#sec-jobs
158
+ fn enqueue_job (self : Rc <Self >, job : Job , context : & mut Context );
159
+
160
+ /// Runs all jobs in the executor.
161
+ fn run_jobs (self : Rc <Self >, context : & mut Context ) -> JsResult <()>;
162
+
163
+ /// Asynchronously runs all jobs in the executor.
164
+ ///
165
+ /// By default forwards to [`JobExecutor::run_jobs`]. Implementors using async should override this
166
+ /// with a proper algorithm to run jobs asynchronously.
167
+ async fn run_jobs_async (self : Rc <Self >, context : & RefCell <& mut Context >) -> JsResult <()>
168
+ where
169
+ Self : Sized ,
170
+ {
171
+ self . run_jobs (& mut context . borrow_mut ())
172
+ }
173
+ }
174
+ ```
62
175
63
- We also add support for span nodes in our parser and AST. Span nodes mark the start and
64
- end index in the source code for a specific token.
176
+ As you can probably tell, we made a lot of changes on the ` JobExecutor ` :
65
177
66
- As a result, this release of Boa supports error backtraces when an exception is thrown
67
- as seen below.
178
+ TODO
68
179
69
- ![ backtrace-example ] ( ./img/backtrace.gif )
180
+ ### Revamped ` ModuleLoader `
70
181
71
- This feature will greatly help with debugging errors when using Boa.
182
+ TODO
72
183
73
- ### Specification updates
184
+ ### Built-ins updates
74
185
75
186
#### Set methods
76
187
77
- This release adds support for the new set methods added in ES2025.
188
+ This release adds support for the new set methods added in ECMAScript's 2025
189
+ specification.
78
190
79
191
The new methods are:
80
192
@@ -101,15 +213,18 @@ console.log(x[1]); // 42.125
101
213
102
214
#### Error.isError
103
215
104
- This release adds support for ` Error.isError ` , which is accepted for ES2026.
216
+ This release adds support for ` Error.isError ` , which will be introduced in
217
+ ECMAScript's 2026 specification.
105
218
106
219
``` javascript
107
- let check = Error .isError (new Error ()); // returns true
220
+ console .log (Error .isError (new Error ())); // true
221
+ console .log (Error .isError ({ __proto__: Error .prototype })); // false
108
222
```
109
223
110
224
#### Math.sumPrecise
111
225
112
- This release adds support for ` Math.sumPrecise ` , which is accepted for ES2026.
226
+ This release adds support for ` Math.sumPrecise ` , which will be introduced in
227
+ ECMAScript's 2026 specification.
113
228
114
229
We've opted for using the new [ ` xsum ` ] ( https://crates.io/crates/xsum ) summation algorithm
115
230
for the underlying implementation.
@@ -119,6 +234,13 @@ let sum = Math.sumPrecise([1e20, 0.1, -1e20]);
119
234
console .log (sum); // 0.1
120
235
```
121
236
237
+ #### Atomics.waitAsync
238
+
239
+ TODO
240
+
241
+
242
+ #### Array.fromAsync
243
+
122
244
## Boa Runtime
123
245
124
246
Work on Boa's runtime crate has continued with additional APIs added.
@@ -146,7 +268,7 @@ With this release, Boa's `JsValue` will use nan-boxing by default. The NaN boxin
146
268
increased memory and runtime performance over the older enum.
147
269
148
270
As a note, the current implementation is not compatible with all platforms. While we hope
149
- to address this in the future. The legacy enum JsValue will be available via the ` jsvalue-enum `
271
+ to address this in the future, the legacy enum JsValue will be available via the ` jsvalue-enum `
150
272
feature flag.
151
273
152
274
Unfamiliar with NaN Boxing? We won't go over it in depth here, but we recommend
0 commit comments