Hi!
I'm pretty invested in seeing https://github.com/bytecodealliance/StarlingMonkey/pull/103 land and in general making it so we can do more StarlingMonkey work directly in Rust. I'm wondering what I can do to support getting this moving forward. My current priority is getting Node.js and WinterTC stuff landed, and I would much rather we build all those bits of new code on top of Rust instead of having to go back later and rewrite everything.
/cc @Till Schneidereit
Resurrecting this a bit: I was told someone had actually picked this up recently? Is that right?
Yup! @Till Schneidereit is working on it :)
I might be a bit out of date but you can see the work here, under the working name starlingshell:
https://github.com/tschneidereit/StarlingMonkey/tree/better-rust-builtins/runtime/crates/starlingshell
Till took the time to integrate and reuse a bunch of upstream implementations which has been great, but IIRC at the last JS meeting he was mentioning how there's a lot more to be done, I'm sure he could use a hand!
(please feel free to correct me Till!)
Victor Adossi said:
Yup! Till Schneidereit is working on it :)
I might be a bit out of date but you can see the work here, under the working name
starlingshell:
https://github.com/tschneidereit/StarlingMonkey/tree/better-rust-builtins/runtime/crates/starlingshellTill took the time to integrate and reuse a bunch of upstream implementations which has been great, but IIRC at the last JS meeting he was mentioning how there's a lot more to be done, I'm sure he could use a hand!
(please feel free to correct me Till!)
Hey, this would be great to get @Kat Marchán (they/she) 's help, and @Tomasz Andrzejak as I understand it is also available to make this work.
@Till Schneidereit is there a way to break this work up and have all three be productive in parallel? From what i understand, this would be the BOMB to make work properly and would advance quite a bit in several dimensions......
I'm happy to find ways to help as long as I'm not stepping on toes or duplicating work.
hey all, I'm very sorry for not giving an update on this in such a long time!
The tl;dr of the current situation is that I took on more than I should've in trying to port much of Servo to WASI without threads at the same time as trying to understand WASIp3 async, and at some point got too lost to make meaningful progress anymore.
The idea still seems right to me: instead of reinventing much of what Servo had to invent for good JS bindings support, we'd reuse theirs—and in the process get access to their implementation of many/most of the builtins relevant to WinterTC compatibility. And I think we can still get there—but we can't block progress on, well, anything on it further.
So, I sort of hit reset on this a couple of weeks ago by asking @Joel Dice to look into the WASIp3 part along with a replacement for ComponentizeJS to support custom WIT interfaces. This work to some degree naturally introduces some amount of Rust support, because it depends on wit-dylib.
In parallel to this, I'm working on porting the WIT bindings the current C++ implementation relies on to Rust as step one of then porting them from WASIp2 to WASIp3. In doing so, I'm doing the minimal amount of work needed to support both the existing C++ builtins as well as new Rust builtins. We can then iterate on providing better abstractions for writing Rust builtins, but will have the basis for starting to write any at all
oh, and based on all this I'll return to integrating Servo builtins, with the goal of ultimately replacing the C++ builtins we currently have entirely. But that can be a gradual process, instead of the all-at-once one I took before
This is what I've done so far: https://github.com/dicej/componentize-js. One test passes :partying_face:
I had to pause that work temporarily last week to work on a few more urgent things, but hope to come back to it by the end of this week. I don't know that it will benefit from more people working on it quite yet, but I expect that will change after a couple of weeks of focused work.
as we have plenty of interest, when @Joel Dice you find a thing to fork off for someone else, do ping!!!! And thanks a ton Till, for all the hard work -- it teaches everyone.
I'm getting closer to that point: I have a version of StarlingMonkey working locally that passes the test suite using WASIp3. There are some bugs around concurrent reuse to be worked out and lots of cleanup to be done, but it's progressing well. That port is using Rust for the WIT bindings, instead of going through wit-bindgen's C backend, as described before.
Once that fully works, I'll port the core of the current runtime over to Rust (which is a smaller task than it'd seem), while still supporting the current C++ builtins.
And then, I'll provide a recipe for creating new Rust builtins with reasonable abstractions—at which point I think we should be in good shape for others to start writing builtins.
I'll provide another update on all this early next week
It's almost midnight on Tuesday my time, so in a few minutes "early next week" will have passed. So: an update. I don't yet have code to share, but I have a thing working that has a core Rust runtime with a clean build system without CMake, built on mozjs.
It has the wiring to set up to compile and install existing C++ builtins, with the console one integrated and fully working. I.e., it can print "Hello, world" and things.
It can also load JS files as either legacy scripts or modules, and for the latter it uses Oxc Resolver to do Node-compatible resolution—a significant improvement over the previous state of affairs!
It does not yet have any HTTP support, and async stuff is rudimentary. But I have separate PoCs for both of those pieces that I'm now working on integrating to enable more builtins.
Currently all of this compiles natively and to WASIp2. I would like to keep it that way by introducing a native implementation for all APIs that on Wasm will use WASIp3 imports, but I'm not yet certain that will work out.
So far for getting back to parity(++). But this thread is ultimately about how to add new builtins. And do I ever have a story for that!
I put together a bunch of proc macros that make it very easy to define builtin classes and modules, with inheritance, instances of one class holding references to other classes as automatically managed GC pointers and all that good stuff. The code using these can be very clean and idiomatic, but still has full flexibility WRT how to interpret and create JS values, etc.
Here's a class definition:
#[jsclass]
struct MyClass {
data: String,
}
#[jsmethods(rename_all = "camelCase")]
impl MyClass {
#[constructor]
fn new(data: String) -> Self {
Self { data }
}
#[getter]
fn data(&self) -> String {
self.data.clone()
}
}
The result is usable from both JS
let my = new MyClass("foo");
let data = my.data; // "foo"
... and Rust:
let my = MyClass::new(cx, "foo".to_string());
let data = my.data; // String("foo"), which can and should probably be improved
And here's a module:
#[jsmodule(rename_all = "camelCase")]
mod math_utils {
pub const PI: f64 = std::f64::consts::PI;
pub const MAX_VALUE: f64 = 1000.0;
pub fn add(a: f64, b: f64) -> f64 {
a + b
}
pub fn multiply(a: f64, b: f64) -> f64 {
a * b
}
pub fn safe_divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("Division by zero".to_string())
} else {
Ok(a / b)
}
}
}
Use in JS:
import {safeDivide} from "MathUtils";
safeDivide(1, 0); // Throws an exception. Proper exn types still to be done
that's it for now, next update soon, but probably after the Plumbers Summit
this is super cool! Thanks for the update (and the awesome work)!
This looks amazing :star_struck:
Thank you both! It's been way way way too long a time coming, which I apologize for
all in on the complements here, @Till Schneidereit - great stuff. :-)
I made great progress this week on the new wit-dylib-based componentize-js. It's "feature complete" in the sense that it can handle arbitrary WIT worlds, including async imports and exports, futures, and streams, but still needs some work to make it usable and idiomatic. See the README.md for the current TODO list.
Till is going to post an update on his StarlingMonkey+Servo work soon, and we're going to do some planning early next week to determine how to integrate these projects together.
Hey all! How's it been doing with all this? Anything I can do to help?
@Joel Dice have you heard any news? It's been 2 months since your last message :\
Hey Kat, I'm very very sorry for being so unresponsive :frown: I feel terrible for not being able to prioritize getting the rewrite over the finish line for way too long as well.
So, an update: I have most everything sorted out and feel very good about where things stand wrt defining builtins (WebIDL and otherwise) and GC rooting. The thing I'm currently working through and consider the last big blocker before work on various builtins can really commence is the event loop. I have something on my machine for that that works both natively and under WASIp3, but I don't yet feel good enough about it to push it to the repo. That has been the state of affairs for about 3 weeks now, during which I've not for the life of me been able to find the focus time (and, well, focus) to get it to a good state. My plan is to reserve time where I cancel all meetings for a few days starting either tomorrow or early- mid-next week. At which point I'll share another update here, hopefully with some language around how to finally dive in and start implementing Stuff
Thanks for the update! looking forward to it. I actually cloned the repo and started looking around and getting a feel for how writing builtins would work by starting to write Blob (which, ofc, I can't get working without that event loop...). I really like the APIs!
that's really encouraging to hear! Incidentally I have a draft of a File & Blob implementation locally, but I don't think it's particularly good
probably better than mine haha but I was just doing it to learn anyway
mainly I needed some beefier builtins to play around with to explore sharp edges of the APIs
since it's one of the simpler ones.
heh
I guess aside from it depending on a few other types like ArrayBuffers and such
(and Promise)
incidentally omg the JSPromise API is :chefkiss:
right, yeah. Part of what I have locally is (slightly) better abstractions for the required SpiderMonkey APIs
for context: we've been having a lot of memory-related issues over in the fastly SDK, including rooting issues as we start testing with high GC zeal. Some of these have actually affected customers. Having to mostly not worry about this stuff would be... very nice.
that's what I invested most effort into: having a really robust rooting story. I'm confident that by now it's actively hard to introduce rooting hazards without them being found by the static analysis or running tests with GC zeal enabled
as a side note, and a thing I hope you'll keep in mind esp now that you're working on the event loop: we've added a reusable sandbox feature to our SDK, which means that a single instance will potentially handle multiple requests before shutting down. There was some amount of global state we retained on the SDK side itself (I don't think any of it was particularly in SM), but if you find yourself doing global state that might trip over itself if the process is long-running, that might be something to watch out for. I still assume it's mostly a thing I'll need to keep in mind when porting our SDK itself, though.
yeah the rooting stuff looks really cool. I'm v excited.
oh yeah, that's very much top of mind for me: we're planning to move to reuse for P3 more generally
we're also eventually, moooooore distant future, thinking of how we might end up componentizing SM itself.
we've finally got components going and being able to just have SM be something we link to will be great (but it probably has long-term requirements like callbacks, which don't exist in WIT yet)
but that'll be great once we have it :)
ah, there might be a shorter path to achieving the goals here: instead of linking to SM as a separate component, it could be linked as a shared library, which can then be deduplicated across all usages in a process. componentize-js is already set up to link SpiderMonkey that way, and wit-dylib more generally is meant to be used that way. Wasmtime also already has support for adding shared libraries to the linker from external-to-the-component preinitialized instances, so the rest is a matter of deployment pipelines and all that. Not trivial, but at least the core platform parts are in place
oh interesting
I finally landed a first version of an event loop. Or rather the first iteration that I felt has enough value to make public.
It has a single abstraction for an async event loop working on native platforms with an async runtime like Tokio and on WASIp3 with stackless async. The way it's meant to be used is that a separate event loop is created for each incoming event, so that e.g. multiple incoming HTTP requests are handled as individual sets of tasks. Obviously nothing prevents JS from e.g. starting a fetch from under one incoming event and awaiting the response from under another, but we can at least do as good a job as is possible as long as developers keep clean separation themselves. This all will help with observability, for example.
It also forms the basis for debugging, where separate event loops do have to be enforced, and we must prevent accidentally resuming processing of a content event loop from under the debugger. (But want to be able to use async APIs in the debugger itself, which current StarlingMonkey doesn't support, because it only has a single event loop.)
The implementation also supports interest management, where an event loop won't continue running if there's nobody around to listen to its outcomes, which will become relevant with componentize-js integration.
I have various half-baked things locally that make use of this, but none of it is in a state where it could land yet, so I'll work on that next. I won't rule out changes to the setup here when it starts being used in actual anger, but my hope at least is that no radical redesign is in the cards for the time being.
this is super exciting! thank you for the update!
Last updated: Jun 01 2026 at 09:49 UTC