Kiesel Devlog #14: Release 0.3.0
Published on 2026-07-17.It's a lovely day 1 at Electromagnetic Field and I spent a few hours between talks and watching Hackers (1995) to release Kiesel 0.3.0! It contains 76 commits across two months, see the changelog for the full list of user-facing changes.
Performance
This release is a big leap for real-world JS performance in Kiesel — mostly thanks to object layout improvements and a basic implementation of fast local variables, the v8-v7 benchmark runs 33% faster and uses 42% less memory:
Benchmark 1 (3 runs): kiesel-0.2.0 v8-v7.js
measurement mean ± σ min … max outliers delta
wall_time 88.5s ± 2.68s 87.0s … 91.6s 0 ( 0%) 0%
peak_rss 388MB ± 305KB 388MB … 388MB 0 ( 0%) 0%
cpu_cycles 555G ± 17.3G 545G … 575G 0 ( 0%) 0%
instructions 1.07T ± 21.9G 1.05T … 1.09T 0 ( 0%) 0%
cache_references 12.2G ± 427M 11.7G … 12.5G 0 ( 0%) 0%
cache_misses 1.55G ± 55.2M 1.50G … 1.61G 0 ( 0%) 0%
branch_misses 561M ± 20.9M 539M … 581M 0 ( 0%) 0%
Benchmark 2 (3 runs): kiesel-0.3.0 v8-v7.js
measurement mean ± σ min … max outliers delta
wall_time 59.0s ± 1.08s 58.3s … 60.3s 0 ( 0%) ⚡- 33.3% ± 5.2%
peak_rss 224MB ± 1.24MB 223MB … 225MB 0 ( 0%) ⚡- 42.2% ± 0.5%
cpu_cycles 343G ± 3.21G 341G … 347G 0 ( 0%) ⚡- 38.2% ± 5.1%
instructions 759G ± 5.22G 755G … 765G 0 ( 0%) ⚡- 29.2% ± 3.4%
cache_references 8.60G ± 234M 8.33G … 8.75G 0 ( 0%) ⚡- 29.3% ± 6.4%
cache_misses 866M ± 5.70M 862M … 872M 0 ( 0%) ⚡- 44.0% ± 5.8%
branch_misses 442M ± 8.16M 436M … 451M 0 ( 0%) ⚡- 21.3% ± 6.4%Kiesel now outperforms Boa, the most popular Rust JS engine, on every benchmark of the v8-v7 suite as can be seen on their benchmarks page.
Explicit Resource Management
This is a TC39 proposal that reached stage 4 in May. using statements provide syntax for managing disposable resources, and the new DisposableStack and AsyncDisposableStack builtins can be used for more complex use cases. See MDN for more details.
class Disposable {
constructor(name) {
this.name = name;
console.log(`${this.name} constructed`);
}
[Symbol.dispose]() {
console.log(`${this.name} disposed`);
}
}
{
using stack = new DisposableStack();
const d1 = stack.use(new Disposable("d1"));
const d2 = stack.use(new Disposable("d2"));
{
using d3 = new Disposable("d3");
}
using d4 = new Disposable("d4");
}$ kiesel main.js
d1 constructed
d2 constructed
d3 constructed
d3 disposed
d4 constructed
d4 disposed
d2 disposed
d1 disposedError.prototype.stack
Another recent stage 4 proposal that has long been present in all major engines as a non-standard feature (MDN). Error.prototype.stack provides an implementation-defined string representing the error stack trace, in Kiesel's case it matches the output of uncaught exceptions:
function bar() {
throw new Error("Oops!");
}
function foo() {
bar();
}
// Caught
try {
foo();
} catch (e) {
console.log(`Caught exception: ${e.stack}`);
}
// Uncaught
foo();$ kiesel main.js
Caught exception: Error: Oops!
at fn bar
at fn foo
Uncaught exception: Error: Oops!
at fn bar
at fn fooDestructuring Assignment Patterns
There was partial support for this using lowering of binding patterns already, but assignment patterns support additional syntax:
> const o = {}, a = [];
undefined
> [o.foo, a[0]] = [123, 456];
[ 123, 456 ]
> o
{ "foo": 123 }
> a
[ 456 ]
>This leads to unreadable code pretty quickly and I haven't seen it much in the wild but we have it now :^)
Handling of Invalid UTF-8 in Scripts
Kiesel now normalizes invalid UTF-8 input using replacement characters instead of rejecting it outright, thus allowing arbitrary byte sequences in comments. This can be used for polyglot programs, for example: