* The other being Google's Closure Compiler, which probably isn't relevant to you as it assumes that its output has to run on existing engines in browsers.
Thank you so much <3
I'm aiming for something like 75-85% this year; basically get iterators properly done (they're in the engine but not very complete yet), implement ECMAScript modules, and then mostly focus on correctness, builtins, and performance improvements after that. 99% would perhaps be possible by the end of next year, barring unforeseeable surprises.
For the foreseeable future the aim will be rather on the QuickJS/DuckTape level than beating V8. But! That is only because they need to be beat before V8 can be beaten :)
I'm not rushing to build a JIT, and I don't even have exact plans for one right now but I'm not barring it out either.
If my life, capabilities, and external support enable it then I do want Nova to either supplant existing mainstream engines, or inspire them to rethink at least some of their heap data structures. But of course it is fairly unlikely I will get there; I will simply try.
So the project started as a "let's write a better JS engine than V8 in Rust!" kind of joke. Beyond that, I personally wanted to write in Rust, and it turns out that with sufficient abuse[1], the Rust borrow checker can be used to perform GC safety checks at compile time. This means that Nova can avoid rooting a lot of values during runtime because at compile time the borrow checker has already ensured that those values will never be used after a GC safepoint.
This is something that would not be feasible in other languages without significant custom checking infrastructure. As an example, Firefox / SpiderMonkey has a custom linter that checks that "no-GC" functions cannot accidentally call back into "GC" functions, but it is roughly a full-program analysis task and the checker is hand-written, custom code that sometimes lacks special cases for this function or that. In Nova, a "GC" function takes a move-only "GcScope" parameter by value (and all GC-able Values observe that value; when the value gets moved to a child call, all those observing Values invalidate; this is that GC safety checking), and "no-GC" functions take a copy "NoGcScope" parameter that can be created from a "GcScope" and that again observes the "GcScope". Through these, the borrow checker becomes the checker for this logic.
[1]: https://fosdem.org/2025/schedule/event/fosdem-2025-4394-abus...
And `1` is?