Stream: StarlingMonkey

Topic: Rust in StarlingMonkey path forward


view this post on Zulip Kat Marchán (they/she) (May 27 2025 at 18:54):

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

This PR introduces rust-bindgen generated bindings for the StarlingMonkey runtime itself, as well as generated bindings and higher-level abstractions building on those for the SpiderMonkey engine. ...

view this post on Zulip Kat Marchán (they/she) (Feb 13 2026 at 22:10):

Resurrecting this a bit: I was told someone had actually picked this up recently? Is that right?

view this post on Zulip Victor Adossi (Feb 14 2026 at 00:49):

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!)

view this post on Zulip Ralph (Feb 17 2026 at 10:12):

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/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!)

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.

view this post on Zulip Ralph (Feb 17 2026 at 10:13):

@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......

view this post on Zulip Kat Marchán (they/she) (Feb 17 2026 at 21:02):

I'm happy to find ways to help as long as I'm not stepping on toes or duplicating work.

view this post on Zulip Till Schneidereit (Feb 18 2026 at 16:01):

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

view this post on Zulip Till Schneidereit (Feb 18 2026 at 16:02):

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

view this post on Zulip Joel Dice (Feb 18 2026 at 16:17):

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.

view this post on Zulip Ralph (Feb 19 2026 at 17:08):

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.

view this post on Zulip Till Schneidereit (Feb 20 2026 at 11:39):

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.

view this post on Zulip Till Schneidereit (Feb 20 2026 at 11:39):

I'll provide another update on all this early next week

view this post on Zulip Till Schneidereit (Feb 24 2026 at 23:00):

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.

view this post on Zulip Till Schneidereit (Feb 24 2026 at 23:04):

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

view this post on Zulip Till Schneidereit (Feb 24 2026 at 23:07):

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

view this post on Zulip Till Schneidereit (Feb 24 2026 at 23:07):

that's it for now, next update soon, but probably after the Plumbers Summit

view this post on Zulip Kat Marchán (they/she) (Feb 24 2026 at 23:36):

this is super cool! Thanks for the update (and the awesome work)!

view this post on Zulip Tomasz Andrzejak (Feb 25 2026 at 07:32):

This looks amazing :star_struck:

view this post on Zulip Till Schneidereit (Feb 25 2026 at 11:22):

Thank you both! It's been way way way too long a time coming, which I apologize for

view this post on Zulip Ralph (Feb 28 2026 at 11:33):

all in on the complements here, @Till Schneidereit - great stuff. :-)

view this post on Zulip Joel Dice (Mar 05 2026 at 17:18):

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.

view this post on Zulip Kat Marchán (they/she) (Apr 27 2026 at 16:42):

Hey all! How's it been doing with all this? Anything I can do to help?

view this post on Zulip Kat Marchán (they/she) (May 05 2026 at 15:59):

@Joel Dice have you heard any news? It's been 2 months since your last message :\

view this post on Zulip Till Schneidereit (May 06 2026 at 11:37):

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

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:22):

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!

view this post on Zulip Till Schneidereit (May 06 2026 at 14:23):

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

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:24):

probably better than mine haha but I was just doing it to learn anyway

view this post on Zulip Till Schneidereit (May 06 2026 at 14:24):

mainly I needed some beefier builtins to play around with to explore sharp edges of the APIs

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:24):

since it's one of the simpler ones.

view this post on Zulip Till Schneidereit (May 06 2026 at 14:24):

heh

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:25):

I guess aside from it depending on a few other types like ArrayBuffers and such

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:25):

(and Promise)

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:25):

incidentally omg the JSPromise API is :chefkiss:

view this post on Zulip Till Schneidereit (May 06 2026 at 14:25):

right, yeah. Part of what I have locally is (slightly) better abstractions for the required SpiderMonkey APIs

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:27):

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.

view this post on Zulip Till Schneidereit (May 06 2026 at 14:30):

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

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:30):

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.

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:31):

yeah the rooting stuff looks really cool. I'm v excited.

view this post on Zulip Till Schneidereit (May 06 2026 at 14:31):

oh yeah, that's very much top of mind for me: we're planning to move to reuse for P3 more generally

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:32):

we're also eventually, moooooore distant future, thinking of how we might end up componentizing SM itself.

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:33):

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)

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:33):

but that'll be great once we have it :)

view this post on Zulip Till Schneidereit (May 06 2026 at 14:37):

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

view this post on Zulip Kat Marchán (they/she) (May 06 2026 at 14:38):

oh interesting

view this post on Zulip Till Schneidereit (May 19 2026 at 09:52):

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.

view this post on Zulip Kat Marchán (they/she) (May 19 2026 at 19:45):

this is super exciting! thank you for the update!

view this post on Zulip Kat Marchán (they/she) (Jun 09 2026 at 21:27):

hey @Till Schneidereit I saw you landed Streams! Congratulations, and amazing work. What a beast of a commit hahaha

view this post on Zulip Kat Marchán (they/she) (Jun 09 2026 at 21:27):

I'm still trying to figure out how to even write Blob lmao (JSPromise isn't working right for me yet)

view this post on Zulip Kat Marchán (they/she) (Jun 09 2026 at 22:30):

heh. Posted too soon. I figured out how to implement Blob#array_buffer() :)

view this post on Zulip Kat Marchán (they/she) (Jun 09 2026 at 22:32):

I know you have an ongoing Blob implementation and it'll probably be way better than mine but Blob is a great builtin to just start familiarizing myself with the APIs so it's been a good exercise. The ergonomics feel pretty good, although it was confusing figuring out what I should use when (JSPromise? What's this Stack thing? Are things safely rooted or am I doing a Danger? How do Scopes work?)

    #[method(name = "arrayBuffer")]
    pub fn array_buffer(&self, scope: &'s Scope<'_>) -> Promise<'s> {
        ArrayBuffer::with_data(scope, &self.data().buffer[..])
            .map(|b| b.handle())
            .map(|abuf| {
                Object::from_handle(abuf)
                    .expect("promise Object")
                    .cast::<Promise>()
                    .expect("Promise")
            })
            .unwrap_or_else(|_| {
                Promise::new_rejected_with_pending_error(scope).expect("rejected promise")
            })
    }

view this post on Zulip Till Schneidereit (Jun 10 2026 at 18:34):

Thank you, @Kat Marchán (they/she)! :heart: It's certainly been a bunch of work, but I'm pleased with how it turned out. I also have fetch and FetchEvent implementations mostly ready, after co-developing them with streams for a while. Need more cleanup and thorough vetting before landing those, though.

Also note the docs/rooting.md file I added, which hopefully will shed light on some somewhat mysterious parts.

As for promises, the streams PR should've improved support for those quite meaningfully. Without testing things, I think roughly this code should now work:

    #[method] // Note: `name` is only needed if automatic camel-casing doesn't result in the right name, e.g. for `toJSON`
    pub fn array_buffer(&self, scope: &'s Scope<'_>) -> Result<Promise<'s>, ExnThrown> {
        let buffer = ArrayBuffer::with_data(scope, &self.data().buffer[..])?;
        let val = scope.root_value(buffer.value());
        Promise::call_original_resolve(scope, buffer.to_jsval(scope))
    }

Again, untested, but I think it should work. The tl;dr is that Result<Promise, Err> returning methods get trampolines generated that automatically convert Err into a rejected promise. And yes, I know this is somewhat magical, but at least to me the ergonomics make it worth it. I suspect we'll want to iterate on some of this over time, though

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:24):

This is pretty cool, thanks! I'm slowly making my way through the Blob impl. I'll look into rewriting what I have so far to integrate these bits and let you know how it all feels :)

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:28):

@Till Schneidereit I have a question about fetch and co: at Fastly, our JS SDK basically copies over the entirety of the fetch section of StarlingMonkey and adds our Fastly-specific behavior by patching that code at that level, and then replacing the builtins. Is that still going to be the strategy this time around, or are you planning on having hooks for implementers? (I assume you're familiar with what I'm talking about, and these kinds of hooks are probably something any SM-NG users would need to have, right?)

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:33):

I think your situation, which I am familiar with indeed, is actually a bit unusual, in that all other users I'm aware of use the WASI implementation directly. So if I'm perfectly honest I haven't thought about it too much so far.

However, my hope is that this should be easier to cater to in -NG than before, since I'm keeping the native version working as I go. In fact, I got a lot of tests for both fetch and FetchEvent passing today on both WASIp3 and native. I'm trying to keep platform details abstracted away where I can, so hopefully you should be able to slot in a different implementation for your platform. Actually making all that configurable will be a bit of work, which we should start hashing out once I have the networking stuff landed

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:35):

oh wait, you're doing wasip3

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:35):

which means we might "just" be able to use wasi-http under the hood and not have this problem?

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:36):

this is using wasi-http@0.3.0, so if you support that, you should be good

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:36):

I think the plan is for us to support that in the relatively near future, yes

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:37):

nice!

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:37):

cc @Peter McInerney @Dan Gohman

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:37):

at least "support" in the sense of having worked on it in some capacity. I don't know when it would land in the fleet or how much I can talk about that. Dan would know more :)

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:38):

heh, fair

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:38):

but either way: if you end up needing to, adding a different backend should hopefully be much easier than before

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:38):

speaking of which, this is way further along than I thought it was! Do you have any inkling of when you're hoping to have it be ready enough to consider a beta of some sort?

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:39):

just so like, I can give folks on my end an idea of timelines, if that's something that makes sense right now

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:39):

"no" is a valid answer haha

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:41):

there's one big thing missing before I feel I can really give any reliable information on that: integration with the new componentize-js @Joel Dice created. If that goes smoothly, we might not be too far off, because I don't think adding the remaining bits on top of fetch and FetchEvent to get to parity with existing SM will take all that much work. (Perhaps with the exception of debugging, though even that might not be too hard)

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:42):

oh awesome. Yeah, I was thinking "eh maybe end of year or early next year"

view this post on Zulip Kat Marchán (they/she) (Jun 10 2026 at 19:42):

but like, I can probably start working on building the new fastly sdk on top of this pretty soon, actually

view this post on Zulip Till Schneidereit (Jun 10 2026 at 19:43):

I was just talking to folks at Akamai about exactly that earlier today :slight_smile:

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 19:55):

    #[method]
    pub fn array_buffer(&self, scope: &'s Scope<'_>) -> Result<Promise<'s>, ExnThrown> {
        let buffer = ArrayBuffer::with_data(scope, &self.data().buffer[..])?;
        Promise::call_original_resolve(scope, buffer.to_jsval(scope).map_err(|e| e.throw(scope))?)
    }

This was what I managed to get compiling, btw :)

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 19:55):

I'm finding myself doing a lot of .to_jsval(scope).map_err(|e| e.throw(scope))? though. I wonder if I'm holding it wrong.

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:28):

ah, yeah. I've been thinking that a lot of things that currently take HandleValue should be changed to take impl ToJSVal instead. Probably including most or all methods on Object<'_>, for example

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:29):

and return T: FromJSVal<'s, Config = ()>

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:30):

that would be smooth. Turning those ConversionErrors into ExnThrown is usually the right thing, right?

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:30):

... though that one is a bit less straightforward, because it'd require ?? in lots of places

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:31):

really depends on where you're using it. Throwing an exception is more expensive than reporting an error, so if you're in Rust code that might not ultimately report that exact error to JS, turning it into ExnThrown would be wasteful. That's the entire reason why ConversionError exists, really

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:34):

that makes sense. But in places like these where you're writing basically js methods, you usually wanna throw, I assume

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:34):

I'm not sure how else you would handle that error in this case

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:34):

#[method]
    pub fn array_buffer(&self, scope: &'s Scope<'_>) -> Result<Promise<'s>, ExnThrown>

Another thing I've been thinking of that might be interesting is to also support something like

#[async_method]
    pub fn array_buffer(&self, scope: &'s Scope<'_>) -> Result<OkType, ErrType>

... and have that automatically resolve or reject a new promise.

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:36):

nice

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:37):

I'm not sure how else you would handle that error in this case

The key reason is wanting to give a better error message that mentions the actual callsite. E.g. something like "Foo.method: 'bar' must be a number"

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:37):

ahh

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:38):

yeah I wasn't sure how those kinds of errors were getting generated. I assumed .throw(scope) was doing some kind of magic

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:38):

hmm, now that you mention it :grimacing:

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:39):

I do wonder if there might be value in storing the last invoked function's name on the scope and using that in error messages, at least optionally

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:39):

:))))

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:39):

but I can imagine that that's not always what you want, and I'd have to think about the overhead a bit

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:40):

yeah, perf is gonna be a big consideration as we switch over to -ng tbh

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:40):

that makes sense, yes

view this post on Zulip Kat Marchán (they/she) (Jun 11 2026 at 20:40):

have you done any testing on that front?

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:41):

not so far, no. I'm currently working on eliminating two extra copies of all bytes passed from JS to Rust for outgoing HTTP requests/responses, so I'm not entirely at that point yet

view this post on Zulip Till Schneidereit (Jun 11 2026 at 20:41):

but for function bindings I tried pretty hard to make things fast, at least

view this post on Zulip Kat Marchán (they/she) (Jun 12 2026 at 18:51):

https://github.com/tschneidereit/starlingmonkey-ng/pull/9 My first (draft) PR is up if you want to see what I've been up to. I'm having trouble figuring out how to use the new streams implementation to efficiently stream out the blob contents. Would be nice to have a util that just spits out a ReadableStream from a buffer.

view this post on Zulip Kat Marchán (they/she) (Jun 17 2026 at 19:14):

what's needed for the componentize-js integration?

view this post on Zulip Kat Marchán (they/she) (Jun 17 2026 at 21:15):

@Joel Dice ^

view this post on Zulip Joel Dice (Jun 17 2026 at 21:17):

Last I heard, @Till Schneidereit was making progress on it. I've offered to help as needed, but he hasn't taken me up on that yet.

view this post on Zulip Kat Marchán (they/she) (Jun 17 2026 at 21:18):

I'm happy to help as needed with that, too, though it might take me time to ramp up. I'm probably more useful writing builtins haha

view this post on Zulip Till Schneidereit (Jun 18 2026 at 10:59):

I went on a bit of a bender late last week and have WIP implementations of fetch, FetchEvent, and componentize-js working locally, passing test suites and all. Plus a design, but not yet implementation, of how to move builtins into their own dylibs that can be optionally loaded, so we get to compose custom runtimes as needed without compiling anything.

I'm currently working through cleaning all that up, starting with the meaningful amount of changes to the js, core-runtime, and starling-macro crates. Will start opening PRs either today or (looking at my meeting schedule: more likely) tomorrow.

view this post on Zulip Till Schneidereit (Jun 18 2026 at 11:00):

the part that I'm least certain of is indeed the componentize-js integration, so I'm planning on getting to that last. But in the meantime, I'm afraid there's not a huge amount that can be parallelized on these things

view this post on Zulip Till Schneidereit (Jun 18 2026 at 11:02):

Would be nice to have a util that just spits out a ReadableStream from a buffer.

@Kat Marchán (they/she) that'll come with the fetch implementation! @Karthik Ganeshram has also been asking for it for the implementation of Text{En,De}coderStream he's working on

view this post on Zulip Tomasz Andrzejak (Jun 22 2026 at 16:33):

Hey! Great stuff! One thing that would be worth to look into and maybe is a good candidate for parallelization is preparing the mozjs/firefox fork that can be used by ng that carries over weval patches and drops stream implementation. Of course, I'm happy to do that :-)

view this post on Zulip Till Schneidereit (Jun 22 2026 at 17:13):

@Tomasz Andrzejak that's a great idea! I have a bunch of patches in my mozjs fork here, which already don't carry the streams implementation. But weval would be great. As would be updating to current mozjs: we're a few patch releases behind at this point.

view this post on Zulip Tomasz Andrzejak (Jun 22 2026 at 18:33):

I'm on it :salute:

view this post on Zulip Tomasz Andrzejak (Jun 30 2026 at 12:27):

This turned out to be much more complex than I thought: SpiderMonkey that mozjs uses is severely trimmed. I can apply weval patches but the problem is rebuilding the IC corpus. That is because IC corpus requires standalone js shell which expects full firefox tree pieces that are missing or handled differently: like js/src/rust, third-party/rust, mozglue/static/rust. Then the next problem is lack of test harness that is used to generate IC files: jstests and jit-tests.

I'm not (yet) sure what the best path forward is, especially to have a reproducible process for future mozjs updates. Should we try to reconstruct enough of a full firefox environment around the mozjs source or maybe carefully apply the mozjs patch stack to firefox.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 12:37):

hum, that's unfortunate indeed :frown: My hunch (without looking at the code, mind) is that much of this is somewhat incidental, and that we'd not necessarily need to do too much work on actually getting the IC corpus built with a small custom embedding instead of the full shell? If that's correct, it might be easiest to build that embedding and import the tests as patches to mozjs

view this post on Zulip Till Schneidereit (Jun 30 2026 at 12:38):

I think we might be able to convince the Servo folks to land at least some of this, fwiw: being able to run tests for mozjs seems valuable in and of itself

view this post on Zulip Tomasz Andrzejak (Jun 30 2026 at 12:50):

So IIUC we could write a rust binary, like ic-corpus-collector that uses mozjs/mozjs-sys + weval directly and use that to run tests? I worry that firefox test-harness is doing much more than just running the files so we won't be able to run full suite with tiny embedding -- but it's worth a shot.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 13:03):

that's what I was thinking of, yeah. And I agree: this only makes sense if we'd not end up reinventing a hugely complex testing harness. We should certainly not do that!

view this post on Zulip Till Schneidereit (Jun 30 2026 at 13:13):

@Tomasz Andrzejak okay, taking a quick look at the test262 setup, I retract what I proposed: I agree that that'd be too much work, and that we should instead make the shell itself work. Which is of course a bit of a bummer :frown:

view this post on Zulip Tomasz Andrzejak (Jun 30 2026 at 16:36):

Yeah, it looks like porting js shell implementation is the way. I tried to write a simple embedding and run few test cases from SM just to see how many ICs it will generate but it only generated 2 ICs -- whereas full suite is producing more than 1000 :melting: . I wonder if wpt coverage wouldn't be much better.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 17:00):

hmm, that's a good question. Though my hunch would be that test262 + jit-tests will generate much better coverage: they're meant to exercise JS execution above all else, after all

view this post on Zulip Kat Marchán (they/she) (Jun 30 2026 at 17:19):

I have a question to discuss: We've had an issue over on the fastly side where we've started deploying reusable sandboxes: that is, where multiple "independent" requests are handled by a single runtime instance. This means that various bits of global state need to be reset between every request in order for things to be safe (for example, the baseUrl). Have you thought of a system for reliably doing this without forgetting? I've had some thoughts but I don't like how it would feel, and I note some of the stuff starlingmonkey-ng does right now is literally with thread_local!, not even static PersistentRootedObject style things.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 17:43):

that's a core part of the design for -ng, yes. Not just for consecutive, but also concurrent reuse. The event loop is set up to use separate JS event loops for separate requests, ensuring that they're cleanly separated. (Or as cleanly as possible: JS can of course do it's own stuff with in-content work queues being processed indiscriminately.) My (still very WIP) local version of FetchEvent support makes use of this, too.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 17:45):

for existing StarlingMonkey, I'd be very careful with reuse, btw: besides the obvious failing asserts, I'd be very wary of not all state being cleared up properly in subtle ways, since the runtime was never properly vetted for it. That's the reason why we never removed the failing asserts: I didn't want to give people a false sense of reuse being supported

view this post on Zulip Kat Marchán (they/she) (Jun 30 2026 at 17:47):

oh yeah I've learned that lesson pretty well, but we've been hardening it for reuse lately for smaller-scale use to see if we can get it to work.

view this post on Zulip Kat Marchán (they/she) (Jun 30 2026 at 17:48):

@Till Schneidereit I brought this up because I noticed WorkerLocation is using a single global var for its url.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 17:50):

that does need to become a TLS thing indeed, yeah. And be updated when switching event loops for the concurrent reuse case

view this post on Zulip Kat Marchán (they/she) (Jun 30 2026 at 17:50):

sounds like my concerns are covered, then, thank you. I did notice that things were a lot more cleanly separated this time, which is great.

view this post on Zulip Till Schneidereit (Jun 30 2026 at 17:51):

well, strictly speaking it doesn't need to be TLS, since we don't actually have multiple threads, but it's certainly a good thing to do regardless

view this post on Zulip Kat Marchán (they/she) (Jul 01 2026 at 17:01):

have there been any thoughts about whether we want to use openssl itself for the crypto stuff, or whether we would go with rustls?

view this post on Zulip Till Schneidereit (Jul 01 2026 at 20:32):

I know that @Karthik Ganeshram has been looking into crypto support, so he might have thoughts on it. I myself haven't dug into it yet, but would assume that rustls might make sense

view this post on Zulip Tomasz Andrzejak (Jul 02 2026 at 08:29):

I'm not sure if rustls can be a practical replacement for openssl. StarlingMonkey uses libcrypto from openssl for crypto algorithms, not TLS stack. Last time I looked into rustls it was TLS protocol library only and not a general purpose crypto library so there was no equivalent to libcrypto API.

The other issue is preserving constant-time behavior which I believe openssl guarantees. Not sure if that's true for rustls?

view this post on Zulip Till Schneidereit (Jul 02 2026 at 10:07):

that's a good point, and I guess I was thinking more of the various RustCrypto crates. On constant-time, note that we're currently not implementing most of SubtleCrypto precisely because we don't have constant-time implementations. OpenSSL doesn't provide any of those for wasm, specifically, and (IIRC) were pretty clear on not being interested in doing so.

RustCrypto would be the better basis for that, since all implementations are explicitly meant to be constant-time regardless of compilation target. Unfortunately most of them haven't undergone full security reviews, and in some cases they're actively known to not be constant-time.

What we'd really need is a replacement for wasi-crypto, but it's not clear to me what the pathway towards that is


Last updated: Jul 29 2026 at 05:03 UTC