Servo and SpiderMonkey
2024-04-15 A new report on Servo’s integration with SpiderMonkey, and our proposal for improving its modularity.
As a web engine, Servo embeds another engine for its script execution capabilities, including both JavaScript and Wasm: SpiderMonkey. One of the goals of Servo is modularity, and the question of how modular it really was with regards to those capabilities came up. For example, how easy would it be for Servo to use Chrome’s V8 engine, or the next big script engine? To answer that question, we’ve written a short report analysing the relationship between Servo and SpiderMonkey.
The problem
Running a webpage happens inside the script
component of Servo; the loading process starts there, and the page continues to run its HTML event loop there.
By its very nature, executing a script from within a webpage requires an integration between the script engine and the web engine that surrounds it.
Anything shared between the two, including the DOM itself and any other construct calling from one into the other, needs to be integrated somehow, and much but not all of that is done via WebIDL.
For example, an integration area that is left for web and script engines to implement as they see fit is that with a garbage collector (see example in Rust for SpiderMonkey).
The need to integrate can result in tight coupling, but the classic ways of increasing modularity — abstractions and interfaces — can be applied here as well, and that is where we found Servo lacking in some ways, but also on the right path. Servo already comes with abstractions and interfaces for a large surface area of its integration with SpiderMonkey, providing ease of use and clarity while preserving boundaries between the two. Other parts of that integration rely on direct, and unsafe, calls into the low-level SpiderMonkey APIs.
The solution
The low-hanging fruit consists of removing these direct calls into low-level SpiderMonkey APIs, replacing them with safe and higher-level constructs. Work on this has started, through a combination of efforts from maintainers and the enthusiasm of community members: eri, tannal, and Taym Haddadi. These efforts have already resulted in the closing of several issues:
- WedIDL: bring dom/bindings/typedarray further in line with spec
- WebIDL: use TypedArray
- Remove create_typed_array from dom/bindings
- WebIDL: use Float32Array in GamePad
- WebIDL: use Float32Array in XRRay
- WebIDL: use Float32Array in XRRigidTransform
- WebIDL: use Float32Array in XRView
- WebIDL impl: remove unsafe JSObject from return value of Document::NamedGetter
Note that the safer higher-level constructs that replace low-level SpiderMonkey API calls are still internally tightly coupled to SpiderMonkey. By centralizing these calls, and hiding them from the rest of the codebase, it becomes possible to enumerate what exactly Servo is doing with SpiderMonkey, and to start thinking about a second layer of abstraction: one that would hide the underlying script engine. An existing, and encouraging, example of such a layer comes from React Native in the form of its JavaScript Interface (JSI).
Call to action
If you are interested in contributing to these efforts, the issues below are good places to start:
- Modular JS/execution engine
- WebIDL impl: remove unsafe JSObject when returning a ReadableStream
- WebIDL impl: remove unsafe JSObject from WebGLExtensionWrapper
- Support FinalizationRegistry
- WebIDL impl: Replace use of NonNull
For more details, read the full report on our wiki.