Skip to content
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
4 changes: 2 additions & 2 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ colors: #in hex code if not noted else

### VERSIONS ###
versions:
scalaJS: 1.19.0
scalaJS: 1.20.1
scalaJSBinary: 1
scalaJS06x: 0.6.33
scalaJS06xBinary: 0.6
scalaJSDOM: 2.8.0
scalaJSDOM: 2.8.1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to the Scala.js release or just a convenience fix?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a drive-by fix.

1 change: 1 addition & 0 deletions _data/library/versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@
- 1.17.0
- 1.18.0
- 1.19.0
- 1.20.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 1.20.0
- 1.20.1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's really .0. This is the list of versions used for the badge selector at https://www.scala-js.org/libraries/ . You can also notice that there is 1.18.0 in that list, although that was also a never-announced version.

168 changes: 168 additions & 0 deletions _posts/news/2025-09-06-announcing-scalajs-1.20.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
layout: post
title: Announcing Scala.js 1.20.1
category: news
tags: [releases]
permalink: /news/2025/09/06/announcing-scalajs-1.20.1/
---


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

This is technically a hotfix patch release for 1.20.0, which was discovered to be [severely broken](https://github.com/scala-js/scala-js/issues/5231), and was therefore never announced.
These release notes therefore present it as a "minor release" compared to 1.19.0.

This release mainly comes with many performance improvements to the WebAssembly and JavaScript backends alike.

As of this writing, the latest versions of Firefox (since v131), Safari (since v18.4) and Chrome (since v137) support all the WebAssembly features required to run Scala.js-on-Wasm.

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, compared to 1.19.0:

* It is backward binary compatible with all earlier versions in the 1.x series: libraries compiled with 1.0.x through 1.19.x can be used with 1.20.x without change.
* It is *not* forward binary compatible with 1.19.x: libraries compiled with 1.20.x cannot be used with 1.19.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.19.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.

## Changes with compatibility concerns

### Drop the `performance.webkitNow` fallback of `System.nanoTime()`

`System.nanoTime()` normally uses the JavaScript API `performance.now()` under the hood if it is available, and `Date.now()` otherwise.
Until Scala.js 1.19.0, it also used `performance.webkitNow()` as possible fallback.
Since browsers supporting `webkitNow()` have been supporting the official `performance.now()` method for more than 10 years now, we have dropped that fallback.

This changed allowed to improve the run-time performance of `System.nanoTime()`.
It may degrade its precision on the old browsers that supported `webkitNow()` but not `now()`.

## Enhancements
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to mention scala-js/scala-js#5228 under changes with compatibility concerns?


### Link-time conditional branching

Thanks to our optimizer's ability to inline, constant-fold, and then eliminate dead code, we have been able to write link-time conditional branches for a long time.
Typical examples include polyfills, as illustrated in the documentation of [`scala.scalajs.LinkingInfo`]({{ BASE_PATH }}/api/scalajs-library/1.20.1/scala/scalajs/LinkingInfo$.html#esVersion:Int):

{% highlight scala %}
if (esVersion >= ESVersion.ES2018 || featureTest())
useES2018Feature()
else
usePolyfill()
{% endhighlight %}

which gets folded away to nothing but

{% highlight scala %}
useES2018Feature()
{% endhighlight %}

when optimizing for ES2018+.

However, this only works because both branches can *link* during the initial reachability analysis. We cannot use the same technique when one of the branches would refuse to link in the first place.
The canonical example is the usage of the JS `**` operator, which does not link below ES2016. The following snippet produces good code when linking for ES2016+, but does not link at all for ES2015:

{% highlight scala %}
def pow(x: Double, y: Double): Double = {
if (esVersion >= ESVersion.ES2016) {
(x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]) // does not link!
.asInstanceOf[Double]
} else {
Math.pow(x, y)
}
}
{% endhighlight %}

Scala.js 1.20.1 introduces `scala.scalajs.LinkingInfo.linkTimeIf`, a conditional branch that is *guaranteed* by spec to be resolved at link-time.
Using a `linkTimeIf` instead of the `if` in `def pow`, we can successfully link the fallback branch on ES2015, avoiding the linking issue in the then branch.

{% highlight scala %}
import scala.scalajs.LinkingInfo.linkTimeIf

def pow(x: Double, y: Double): Double = {
linkTimeIf(esVersion >= ESVersion.ES2016) {
// this branch is only *linked* if we are targeting ES2016+
(x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic])
.asInstanceOf[Double]
} {
Math.pow(x, y)
}
}
{% endhighlight %}

The condition of `linkTimeIf` must be a link-time constant expression.
It can only contain:

* int and boolean literals and constants,
* boolean operators (`!`, `&&` and `||`),
* int comparison operators (such as `==` and `<`),
* link-time properties in the `LinkingInfo` object, notably `esVersion`, `productionMode` and `isWebAssembly`.

### Performance improvements

Scala.js 1.20.1 brings a number of performance improvements compared to 1.19.0.

For both JavaScript and WebAssembly:

* Many low-level methods in `java.lang.Integer`, `Long`, `Float`, `Double`, `Math` and `System` have been micro-optimized.
* Scala `Range`s have a faster initialization path (this particular enhancement is also coming in Scala 2.13.17 for the JVM).

For JavaScript:

* `Long` performance was significantly enhanced, notably additive operations, conversions to strings and conversions to floating-point numbers.

For WebAssembly, performance improvements in the following areas:

* Varargs, when compiled with Scala.js 1.20.0+ on Scala 2, and with the upcoming Scala 3.8.0+ on Scala 3
* `java.util.ArrayList`, `ArrayDeque`, `PriorityQueue` and `java.util.concurrent.CopyOnWriteArrayList`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add scala-js/scala-js#5224 (RedBlackTree.fromOrdered)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this one is quite niche, and it's not easy to describe in simple terms what user-facing API is affected. So I suggest we don't mention it.

* `java.util.RedBlackTree.fromOrdered`
* Startup time in the presence of large arrays of constants
* Generally, fewer hops across the Wasm-JS boundary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add:

  • startup speed thanks to constant arrays in data segments?
  • "general efforts to minimize JS / WASM hops" or something? (I don't think it makes sense to list the specifics, but maybe it's still worth mentioning overall).

## Miscellaneous

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more names for WASM?

### New JDK APIs

This release adds support for the following JDK methods:

* In `java.lang.Math`:
* `multiplyFull`
* `multiplyHigh`
* `unsignedMultiplyHigh`
* In `java.lang.Double`:
* `doubleToRawLongBits`
* In `java.lang.Float`:
* `floatToRawIntBits`
* In `java.lang.Integer` and `java.lang.Long`:
* `compress`
* `expand`

### Improved debugging experience on Wasm

The Wasm backend now emits more debugging information: the names of types, object fields, local variables and global variables.
This improves the debugging experience when targeting WebAssembly.

## Bug fixes

The following bugs have been fixed in 1.20.0 and 1.20.1:

* [#5159](https://github.com/scala-js/scala-js/issues/5159) Regression in v1.19.0: `AssertionError` with module splitting on and optimizer off.
* [#5165](https://github.com/scala-js/scala-js/issues/5165) Wasm: `try..finally` with non-nullable reference type produces invalid code.
* [#5208](https://github.com/scala-js/scala-js/issues/5208) `doubleToLongBits` can observe non-canonical NaN bit patterns.
* [#5231](https://github.com/scala-js/scala-js/issues/5231) `AssertionError`: rolled-back RuntimeLong inlining (regression in 1.20.0).

You can find the full list [on GitHub](https://github.com/scala-js/scala-js/issues?q=is%3Aissue+milestone%3Av1.20.0+is%3Aclosed).
1 change: 1 addition & 0 deletions assets/badges/scalajs-1.20.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.20.1
* [1.20.1 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.20.1/scala/scalajs/js/index.html)
* [1.20.1 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.20.1/)
* [1.20.1 scalajs-javalib-intf]({{ site.production_url }}/api/scalajs-javalib-intf/1.20.1/)
* [1.20.1 scalajs-ir]({{ site.production_url }}/api/scalajs-ir/1.20.1/org/scalajs/ir/index.html)
* [1.20.1 scalajs-linker-interface]({{ site.production_url }}/api/scalajs-linker-interface/1.20.1/org/scalajs/linker/interface/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-interface-js/1.20.1/org/scalajs/linker/interface/index.html))
* [1.20.1 scalajs-linker]({{ site.production_url }}/api/scalajs-linker/1.20.1/org/scalajs/linker/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-js/1.20.1/org/scalajs/linker/index.html))
* [1.20.1 scalajs-test-adapter]({{ site.production_url }}/api/scalajs-sbt-test-adapter/1.20.1/org/scalajs/testing/adapter/index.html)
* [1.20.1 sbt-scalajs]({{ site.production_url }}/api/sbt-scalajs/1.20.1/#org.scalajs.sbtplugin.package)

### 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/)
Expand Down
2 changes: 2 additions & 0 deletions doc/internals/version-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ title: Version history

## Version history of Scala.js

- [1.20.1](/news/2025/09/06/announcing-scalajs-1.20.1/)
- ~~1.20.0~~ ([severely broken](https://github.com/scala-js/scala-js/issues/5231) and therefore never announced)
- [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/)
Expand Down
10 changes: 4 additions & 6 deletions doc/project/webassembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ The Wasm backend emits code with the following requirements:
* Exception handling, including the latest `exnref`-based variant
* The `ESModule` module kind (see [emitting modules](./module.html))

Supported engines include Node.js 23, Chrome, Firefox and Safari.
Node.js and Chrome currently require using some experimental flags (see below).
Supported engines include Node.js 23, Chrome 137, Firefox 131 and Safari 18.4.
Node.js currently requires using some experimental flags (see below).

## Language semantics

Expand Down Expand Up @@ -102,9 +102,7 @@ As mentioned above, Node.js 23 and above requires the following flags:

### 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).
Chrome supports all the features we use since v137.

### Firefox

Expand All @@ -123,7 +121,7 @@ Safari supports all the "always required" features since v18.4.

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).
For applications whose performance is dominated by computions inside Scala code, the Wasm output should be significantly faster than the JS output (geomean 30% 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