Description
Kinda moved from tanishiking/scala-wasm#117
This issue is created as a meta issue to discuss supporting standalone Wasm and the overall approach. For the design and implementation of individual components, separate issues will be created, and the details will be discussed in those respective issues.
The current Scala.js Wasm backend implementation generates a Wasm binary that runs on JavaScript environments such as browsers, Node.js, Deno, and Cloudflare Workers (V8 isolate). However, it does not work on standalone Wasm runtimes such as WasmEdge and wasmtime. Supporting standalone Wasm runtimes would be important to enable Scala to run on Wasm-based cloud/edge computing services, container environments like runwasi, and so on.
To support standalone Wasm runtimes in Scala.js, the following points need to be addressed:
- Conditional Linking: Introduce a link-time condition or mechanism to allow libraries to switch their implementation based on whether the target is JavaScript Wasm or standalone Wasm.
- Alternatively, create a separate Intermediate Representation (IR) specifically for standalone Wasm (let's call it SWIR 😄). This would require libraries to build against standalone Wasm in addition to ScalaJVM, Scala.js, and Scala Native (even if there are no changes needed from the Scala.js version). I'm not sure it's a good idea.
- WASI Support: Implement support for the WASI (at least preview 1 for now) in the
scala.scalajs.wasm
package.- Initial prototype is available in [DO NOT MERGE] Initial memory allocation and WASI support tanishiking/scala-wasm#135
- This will allow libraries to use WASI APIs for their implementations when targeting standalone Wasm.
- Avoid JavaScript Interop in the Backend: When the target is standalone Wasm, the Wasm backend should avoid using JavaScript interop. This will require changes to various parts of the compiler, such as:
- String (and some primitive types) implementation needs to be re-implement in pure-wasm, and re-implement
box/unbox/typeTest
operations._JSStringOps
. - Handling of
fmod
,undefined values
,Object.is
comparison,makeExportDef
function, andclosures
and so on.
- String (and some primitive types) implementation needs to be re-implement in pure-wasm, and re-implement
- Re-implement Libraries: Some parts of the
javalib
andscalalib
need to be re-implemented using pure Scala and Wasm/WASI intrinsics. For example:- Math package could be developed using a pure Scala implementation of the
fdlibm
, similar to how Kotlin/Wasm implemented their math package - Regular Expressions: Scala Native has a pure Scala implementation of regular expressions (a port of
re2j
for Scala), which could be copied or adapted for standalone Wasm in Scala.js? - ExecutionContext: Scala.js impl relies on
Promise
orsetTimeout
, but we need to re-implement in Wasm.- It may not be necessary to support it initially in our stand-alone Wasm backend, until WASI provides more building-blocks in future. ref [Designing an Async Runtime for WASI 0.2 https://blog.yoshuawuyts.com/building-an-async-runtime-for-wasi/]
- System:
PrintStream
should be replaced with WASI'sfd_write
andfd_read
functions.currentTimeMillis
can be implemented using WASI'slock_time_get
function.- and so on.
- There should be much more to consider and re-implement...
- Math package could be developed using a pure Scala implementation of the
Ref
- Make scala-wasm suitable for standalone Wasm VMs tanishiking/scala-wasm#117
- Add WebAssembly Linker Backend (with WasmGC and Wasm ExceptionHandling) #4928
- Make Kotlin/Wasm suitable for standalone Wasm VMs : KT-60278
For non-JS hosts, the benefit for users is to target an entirely new ecosystem. In that scenario, we will want to introduce interop features for at least the Component Model of Wasm, and instead remove the JS interop semantics. In that case, we definitely need IR changes. More critically, we will need a link-time way to reliably (i.e., from a reachability point of view) use one path or another. Either we do this with an entirely different ecosystem and IR (SWIR?), or we amend the Scala.js IR. If amending the Scala.js IR is not too disruptive, this would help adoption of Scala-to-Wasm, as the majority of the Scala.js ecosystem of libraries could be reused as is. We would avoid the slow bootstrap problem entirely.
We discussed one possible link-time dispatch mechanism that would fit in the Scala.js IR: a "link-time if-else". Its conditions would be restricted to a few elementary operations, based on a config dictionary of String->Int. For example it might contain "host"->0 for a JS host and "host"->1 for a WASI host. Or a "isWasm"->0/1, which could be used for code paths known to be better for Wasm than for JS (e.g., manipulating Longs more aggressively, or bit-casting between ints and floats, etc.)