Skip to content

Announcing Scala.js 1.19.0. #654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ colors: #in hex code if not noted else

### VERSIONS ###
versions:
scalaJS: 1.18.2
scalaJS: 1.19.0
scalaJSBinary: 1
scalaJS06x: 0.6.33
scalaJS06xBinary: 0.6
Expand Down
1 change: 1 addition & 0 deletions _data/library/versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
- 1.16.0
- 1.17.0
- 1.18.0
- 1.19.0
192 changes: 192 additions & 0 deletions _posts/news/2025-04-21-announcing-scalajs-1.19.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
layout: post
title: Announcing Scala.js 1.19.0
category: news
tags: [releases]
permalink: /news/2025/04/21/announcing-scalajs-1.19.0/
---


We are pleased to announce the release of Scala.js 1.19.0!

This release comes with significant performance improvements to the WebAssembly backend.
For codebase where performance is dominated by computations (rather than JS interop), you can now expect the Wasm output to be faster than the JS output.

Moreover, as of this writing, the latest versions of Firefox (since v131) and Safari (since v18.4) support all the WebAssembly features required to run Scala.js-on-Wasm.
Chrome still requires a flag to enable exception handling.

If you haven't tried the WebAssembly target yet, now is a good time to do so!

Other highlights:

* Native support for JavaScript `async/await`, through `js.async { ... }` and `js.await(...)`.
* Small code size improvements for the JavaScript target when using SAM lambdas.
* For Wasm only: support for the JavaScript Promise Integration feature (JSPI).

Read on for more details.

<!--more-->

## Getting started

If you are new to Scala.js, head over to [the tutorial]({{ BASE_PATH }}/tutorial/).

If you need help with anything related to Scala.js, you may find our community [in `#scala-js` on Discord](https://discord.com/invite/scala) and [on Stack Overflow](https://stackoverflow.com/questions/tagged/scala.js).

Bug reports can be filed [on GitHub](https://github.com/scala-js/scala-js/issues).

## Release notes

If upgrading from Scala.js 0.6.x, make sure to read [the release notes of Scala.js 1.0.0]({{ BASE_PATH }}/news/2020/02/25/announcing-scalajs-1.0.0/) first, as they contain a host of important information, including breaking changes.

This is a **minor** release:

* It is backward binary compatible with all earlier versions in the 1.x series: libraries compiled with 1.0.x through 1.18.x can be used with 1.19.0 without change.
* It is *not* forward binary compatible with 1.18.x: libraries compiled with 1.19.0 cannot be used with 1.18.x or earlier.
* It is *not* entirely backward source compatible: it is not guaranteed that a codebase will compile *as is* when upgrading from 1.18.x (in particular in the presence of `-Xfatal-warnings`).

As a reminder, libraries compiled with 0.6.x cannot be used with Scala.js 1.x; they must be republished with 1.x first.

## Enhancements with compatibility concerns

### Drop support for non-strict floats

Support for non-strict `Float`s was deprecated 3 years ago in Scala.js 1.9.0.
We now removed that support.
Builds configured to use non-strict floats (with `withStrictFloats(false)`) will refuse to compile.
If that setting is used through an indirect dependency, it will be silently ignored.

Strict floats are almost entirely backward compatible with non-strict floats.
In general, strict floats mandate behavior which non-strict floats leave unspecified (so non-strict floats were always permitted to behave like strict floats).
The only exception is that tests of the form `x.isInstanceOf[Float]` (or `case x: Float =>`) will answer `false` for number values that cannot exactly be represented in a 32-bit `Float`.

We are not aware of any codebase that was ever adversely affected by strict float semantics.

### Deprecate support for targeting ECMAScript 5.1

The support for targeting ECMAScript 5.1 is currently the biggest source of alternative code paths and polyfills in our codebase.
Moreover, the ES 5.1 output does not even have exactly the same semantics as later versions:

* JS classes are not true classes. Notably, that means they cannot extend native ES classes, and they do not inherit static members.
* Top-level exports are declared as vars instead of lets.

10 years after the introduction of ECMAScript 2015, we believe it is time to deprecate the support for targeting ES 5.1, so that we can eventually remove it.
The removal will happen in a future major or minor release of Scala.js.

### Changes to the IR and linker APIs

For tooling authors who directly manipulate the IR and linker APIs, there have been some breaking changes in that area.
This is in line with our version policy for the linker APIs.

The most likely changes you may hit are:

* The `ir.Trees.Closure` node has two additional fields: `flags` and `resultType`.
* All the "well-known" names that were previously in `ir.Names` have been moved to a new object `ir.WellKnownNames`.
* `ir.Types.PrimRef` is not a case class anymore.

## Enhancements

### Native support for JS `async/await`

Note 1: Scala 3 users will have to wait for a future release of Scala 3---likely 3.8.0---to be able to use this new feature.

Note 2: when targeting WebAssembly, this feature requires support for JSPI (JavaScript Promise Integration).
You can [check support for JSPI in various browsers here](https://webassembly.org/features/#table-row-jspi).
On Node.js v23+, this requires the flag `--experimental-wasm-jspi`.

This release adds a pair of primitives, `js.async` and `js.await`, which correspond to the `async/await` feature of JavaScript.
In order to use these functions, you must configure Scala.js to target ECMAScript 2017 or later.
In an sbt build, use the following setting:

{% highlight scala %}
// Target ES 2017 to enable async/await
scalaJSLinkerConfig := {
scalaJSLinkerConfig.value
.withESFeatures(_.withESVersion(ESVersion.ES2017)) // enable async/await
},
{% endhighlight %}

You can then use `js.async { ... }` blocks containing `js.await` calls.
For example:

{% highlight scala %}
val p: js.Promise[String] = downloadSomething()
val result: js.Promise[Int] = js.async {
val text: String = js.await(p)
text.toInt
}
{% endhighlight %}

`js.async { ... }` executes a block of code under a JavaScript `async` context.
The block of code can await `js.Promise`s using `js.await`.
Doing so will continue the execution after the call to `js.await` when the given `Promise` is resolved.
If the `Promise` is rejected, the exception gets rethrown at the call site.

A block such as `js.async { body }` is equivalent to an immediately-applied JavaScript `async` function:

{% highlight javascript %}
(async () => body)()
{% endhighlight %}

`js.async` returns a `js.Promise` that will be resolved with the result of the code block.
If the block throws an exception, the `Promise` will be rejected.

Calls to `js.await` can only appear within a `js.async` block.
They must not be nested in any local method, class, by-name argument or closure.
The latter includes `for` comprehensions.
They may appear within conditional branches, `while` loops and `try/catch/finally` blocks.

### Orphan `await`s in WebAssembly

When compiling for Scala.js-on-Wasm only, you can allow calls to `js.await` anywhere, by adding the following import:

{% highlight scala %}
import scala.scalajs.js.wasm.JSPI.allowOrphanJSAwait
{% endhighlight %}

Calls to orphan `js.await`s are validated at run-time.
There must exist a dynamically enclosing `js.async { ... }` block on the call stack.
Moreover, there cannot be any JavaScript frame (JavaScript function invocation) in the call stack between the `js.async { ... }` block and the call to `js.await`.
If those conditions are not met, a JavaScript exception of type `WebAssembly.SuspendError` gets thrown.

The ability to detach `js.await` calls from their corresponding `js.async` block is a new superpower offered by JSPI.
It means that, as long as you enter into a `js.async` block somewhere, you can *synchronously await* `Promise`s in any arbitrary function!
This is a power of the same sort as Loom on the JVM.
We are looking forward to seeing new libraries built on these primitives to offer efficient and straightforward APIs.

### Performance improvements for WebAssembly

Scala.js 1.19.0 brings significant performance improvements to the WebAssembly output.
Starting with this release, the WebAssembly output is faster than the JavaScript output on our benchmarks (geomean of the running time is 15% lower).

Note that our benchmarks do not measure JavaScript interop in any significant way.
If your application's performance depends mostly on JS interop, the JavaScript output is probably still faster (and will remain so for the foreseeable future).
However, if your bottleneck is in computations inside Scala code, compiling to WebAssembly should now give you a free performance boost.

The WebAssembly target also received additional improvements in terms of code size and run-time memory consumption.

As a reminder, you may read detailed information about [the WebAssembly backend in the docs]({{ BASE_PATH }}/doc/project/webassembly.html).

## Miscellaneous

### New JDK APIs

This release adds support for the following JDK methods:

* Most of `java.util.random.RandomGenerator`

### Improvements to the JUnit interface

Thanks to [`@dubinsky`](https://github.com/dubinsky) for contributing two improvements to the integration of our JUnit implementation with external tooling (in particular Gradle):

* populate `sbt.testing.Event.throwable` on test failure, and
* populate `sbt.testing.Event.duration`.

## Bug fixes

The following bugs have been fixed in 1.19.0:

* [#5131](https://github.com/scala-js/scala-js/issues/5131) Linker not noticing instance test changes
* [#5135](https://github.com/scala-js/scala-js/issues/5135) Deadlock in concurrent initialization of `ir.Names` and `ir.Types`.

You can find the full list [on GitHub](https://github.com/scala-js/scala-js/issues?q=is%3Aissue+milestone%3Av1.19.0+is%3Aclosed).
1 change: 1 addition & 0 deletions assets/badges/scalajs-1.19.0.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions doc/all-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ title: All previous versions of the Scala.js API

## All previous versions of the API

### Scala.js 1.19.0
* [1.19.0 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.19.0/scala/scalajs/js/index.html)
* [1.19.0 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.19.0/)
* [1.19.0 scalajs-javalib-intf]({{ site.production_url }}/api/scalajs-javalib-intf/1.19.0/)
* [1.19.0 scalajs-ir]({{ site.production_url }}/api/scalajs-ir/1.19.0/org/scalajs/ir/index.html)
* [1.19.0 scalajs-linker-interface]({{ site.production_url }}/api/scalajs-linker-interface/1.19.0/org/scalajs/linker/interface/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-interface-js/1.19.0/org/scalajs/linker/interface/index.html))
* [1.19.0 scalajs-linker]({{ site.production_url }}/api/scalajs-linker/1.19.0/org/scalajs/linker/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-js/1.19.0/org/scalajs/linker/index.html))
* [1.19.0 scalajs-test-adapter]({{ site.production_url }}/api/scalajs-sbt-test-adapter/1.19.0/org/scalajs/testing/adapter/index.html)
* [1.19.0 sbt-scalajs]({{ site.production_url }}/api/sbt-scalajs/1.19.0/#org.scalajs.sbtplugin.package)

### Scala.js 1.18.2
* [1.18.2 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.18.2/scala/scalajs/js/index.html)
* [1.18.2 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.18.2/)
Expand Down
1 change: 1 addition & 0 deletions doc/internals/version-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ title: Version history

## Version history of Scala.js

- [1.19.0](/news/2025/04/21/announcing-scalajs-1.19.0/)
- [1.18.2](/news/2025/01/23/announcing-scalajs-1.18.2/)
- [1.18.1](/news/2025/01/09/announcing-scalajs-1.18.1/)
- ~~1.18.0~~ ([severely broken](https://github.com/scala-js/scala-js/issues/5107) and therefore never announced)
Expand Down
31 changes: 21 additions & 10 deletions doc/project/webassembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ The Wasm backend emits code with the following requirements:
* Wasm GC
* Exception handling, including the latest `exnref`-based variant
* The `ESModule` module kind (see [emitting modules](./module.html))
* Strict floats (which is the default since Scala.js 1.9.0; non-strict floats are deprecated)

Supported engines include Node.js 22, Chrome and Firefox, all using some experimental flags (see below).
Supported engines include Node.js 23, Chrome, Firefox and Safari.
Node.js and Chrome currently require using some experimental flags (see below).

## Language semantics

Expand Down Expand Up @@ -75,7 +75,8 @@ scalaJSLinkerConfig := {
jsEnv := {
val config = NodeJSEnv.Config()
.withArgs(List(
"--experimental-wasm-exnref", // required
"--experimental-wasm-exnref", // always required
"--experimental-wasm-jspi", // required for js.async/js.await
"--experimental-wasm-imported-strings", // optional (good for performance)
"--turboshaft-wasm", // optional, but significantly increases stability
))
Expand All @@ -90,29 +91,39 @@ The backend emits ES modules with the same layout and interface as those produce

Here are some engines known to support enough Wasm features.

### Node.js 22
### Node.js 23

As mentioned above, Node.js 23 and above requires the following flags:

* `--experimental-wasm-exnref`: required
* `--experimental-wasm-exnref`: always required
* `--experimental-wasm-jspi`: required to use `js.async`/`js.await`
* `--experimental-wasm-imported-strings`: optional (good for performance)
* `--turboshaft-wasm`: optional, bug significantly increases stability

### Chrome

In `chrome://flags/`, enable ["Experimental WebAssembly"](chrome://flags/#enable-experimental-webassembly-features).

For `js.async`/`js.await` support, also enable ["Experimental WebAssembly JavaScript Promise Integration (JSPI)"](chrome://flags/#enable-experimental-webassembly-jspi).

### Firefox

In `about:config`, enable `javascript.options.wasm_exnref`.
Firefox supports all the "always required" features since v131, with enhanced performance since v134.

For `js.async/js.await` support, go to `about:config`, enable `javascript.options.wasm_js_promise_integration`.
This may require a Firefox Nightly build when you read these lines, although it should be available by default soon.

### Safari

Make sure to *disable* `javascript.options.wasm_js_string_builtins`.
Firefox has two issues with it that break Scala.js ([1919901](https://bugzilla.mozilla.org/show_bug.cgi?id=1919901) and [1920337](https://bugzilla.mozilla.org/show_bug.cgi?id=1920337)).
Safari supports all the "always required" features since v18.4.

`js.async/js.await` is not supported yet, however.

## Performance

Performance of the generated code is currently a hit-or-miss.
Depending on the codebase, it may be several times faster or slower than the JS backend.
If the performance of your application depends a lot on JavaScript interop, the Wasm output may be (significantly) slower than the JS output.

For applications whose performance is dominated by computions inside Scala code, the Wasm output should be significantly faster than the JS output (geomean 15% lower run time across our benchmarks).

Further work on improving performance is ongoing.
Keep in mind that performance work on the Wasm backend is a few months old, compared to a decade of optimizations in the JS backend.
Expand Down
40 changes: 21 additions & 19 deletions doc/semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,6 @@ Instead, it uses the more sensible `java.lang.Void`, as `Void` is the boxed clas
This means that while `java.lang.Void` is not instantiable on the JVM, in Scala.js it has a singleton instance, namely `()`.
This also manifests itself in `Array[Unit]` which is effectively `Array[java.lang.Void]` at run-time, instead of `Array[scala.runtime.BoxedUnit]`.

### Non-strict floats (deprecated; default until Scala.js 1.8.0)

Until v1.8.0, Scala.js underspecified the behavior of `Float`s by default with so-called *non-strict floats*.

Non-strict floats can still be enabled with the following sbt setting:

{% highlight scala %}
scalaJSLinkerConfig ~= { _.withSemantics(_.withStrictFloats(false)) }
{% endhighlight %}

Under non-strict floats, any `Float` value can be stored as a `Double` instead, and any operation on `Float`s can be computed with double precision.
The choice of whether or not to behave as such, when and where, is left to the implementation.
In addition, `x.isInstanceOf[Float]` will return `true` for any `number` values (not only the ones that fit in a 32-bit float).

Non-strict floats are deprecated and will eventually be removed in a later major or minor version of Scala.js.

Enabling non-strict floats may significantly improve the performance (up to 4x for `Float`-intensive applications) when targeting JS engines that do not support [the `Math.fround` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround), such as Internet Explorer (which implies emitting ES 5.1 code).
If you are in that situation, we advise to use `Double`s instead of `Float`s as much as possible.

## Undefined behaviors

The JVM is a very well specified environment, which even specifies how some
Expand Down Expand Up @@ -238,3 +219,24 @@ val <ident> = Value(
We believe that this covers most use cases of
`scala.Enumeration`. Please let us know if another (generalized)
rewrite would make your life easier.

## Historical

### Non-strict floats (removed in Scala.js 1.19.0; default until 1.8.0)

Until v1.8.0, Scala.js underspecified the behavior of `Float`s by default with so-called *non-strict floats*.

Non-strict floats could be enabled with the following sbt setting, until v1.19.0 excluded:

{% highlight scala %}
scalaJSLinkerConfig ~= { _.withSemantics(_.withStrictFloats(false)) }
{% endhighlight %}

Under non-strict floats, any `Float` value can be stored as a `Double` instead, and any operation on `Float`s can be computed with double precision.
The choice of whether or not to behave as such, when and where, is left to the implementation.
In addition, `x.isInstanceOf[Float]` will return `true` for any `number` values (not only the ones that fit in a 32-bit float).

Non-strict floats were deprecated in v1.8.0 and removed in v1.19.0.

Non-strict floats could significantly improve the performance (up to 4x for `Float`-intensive applications) when targeting JS engines that do not support [the `Math.fround` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround), such as Internet Explorer (which implies emitting ES 5.1 code).
If you are in that situation, we advise to use `Double`s instead of `Float`s as much as possible.