|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: Announcing Scala.js 1.20.1 |
| 4 | +category: news |
| 5 | +tags: [releases] |
| 6 | +permalink: /news/2025/09/06/announcing-scalajs-1.20.1/ |
| 7 | +--- |
| 8 | + |
| 9 | + |
| 10 | +We are pleased to announce the release of Scala.js 1.20.1! |
| 11 | + |
| 12 | +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. |
| 13 | +These release notes therefore present it as a "minor release" compared to 1.19.0. |
| 14 | + |
| 15 | +This release mainly comes with many performance improvements to the WebAssembly and JavaScript backends alike. |
| 16 | + |
| 17 | +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. |
| 18 | + |
| 19 | +Read on for more details. |
| 20 | + |
| 21 | +<!--more--> |
| 22 | + |
| 23 | +## Getting started |
| 24 | + |
| 25 | +If you are new to Scala.js, head over to [the tutorial]({{ BASE_PATH }}/tutorial/). |
| 26 | + |
| 27 | +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). |
| 28 | + |
| 29 | +Bug reports can be filed [on GitHub](https://github.com/scala-js/scala-js/issues). |
| 30 | + |
| 31 | +## Release notes |
| 32 | + |
| 33 | +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. |
| 34 | + |
| 35 | +This is a **minor** release, compared to 1.19.0: |
| 36 | + |
| 37 | +* 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. |
| 38 | +* 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. |
| 39 | +* 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`). |
| 40 | + |
| 41 | +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. |
| 42 | + |
| 43 | +## Changes with compatibility concerns |
| 44 | + |
| 45 | +### Drop the `performance.webkitNow` fallback of `System.nanoTime()` |
| 46 | + |
| 47 | +`System.nanoTime()` normally uses the JavaScript API `performance.now()` under the hood if it is available, and `Date.now()` otherwise. |
| 48 | +Until Scala.js 1.19.0, it also used `performance.webkitNow()` as possible fallback. |
| 49 | +Since browsers supporting `webkitNow()` have been supporting the official `performance.now()` method for more than 10 years now, we have dropped that fallback. |
| 50 | + |
| 51 | +This changed allowed to improve the run-time performance of `System.nanoTime()`. |
| 52 | +It may degrade its precision on the old browsers that supported `webkitNow()` but not `now()`. |
| 53 | + |
| 54 | +## Enhancements |
| 55 | + |
| 56 | +### Link-time conditional branching |
| 57 | + |
| 58 | +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. |
| 59 | +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): |
| 60 | + |
| 61 | +{% highlight scala %} |
| 62 | +if (esVersion >= ESVersion.ES2018 || featureTest()) |
| 63 | + useES2018Feature() |
| 64 | +else |
| 65 | + usePolyfill() |
| 66 | +{% endhighlight %} |
| 67 | + |
| 68 | +which gets folded away to nothing but |
| 69 | + |
| 70 | +{% highlight scala %} |
| 71 | +useES2018Feature() |
| 72 | +{% endhighlight %} |
| 73 | + |
| 74 | +when optimizing for ES2018+. |
| 75 | + |
| 76 | +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. |
| 77 | +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: |
| 78 | + |
| 79 | +{% highlight scala %} |
| 80 | +def pow(x: Double, y: Double): Double = { |
| 81 | + if (esVersion >= ESVersion.ES2016) { |
| 82 | + (x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]) // does not link! |
| 83 | + .asInstanceOf[Double] |
| 84 | + } else { |
| 85 | + Math.pow(x, y) |
| 86 | + } |
| 87 | +} |
| 88 | +{% endhighlight %} |
| 89 | + |
| 90 | +Scala.js 1.20.1 introduces `scala.scalajs.LinkingInfo.linkTimeIf`, a conditional branch that is *guaranteed* by spec to be resolved at link-time. |
| 91 | +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. |
| 92 | + |
| 93 | +{% highlight scala %} |
| 94 | +import scala.scalajs.LinkingInfo.linkTimeIf |
| 95 | + |
| 96 | +def pow(x: Double, y: Double): Double = { |
| 97 | + linkTimeIf(esVersion >= ESVersion.ES2016) { |
| 98 | + // this branch is only *linked* if we are targeting ES2016+ |
| 99 | + (x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]) |
| 100 | + .asInstanceOf[Double] |
| 101 | + } { |
| 102 | + Math.pow(x, y) |
| 103 | + } |
| 104 | +} |
| 105 | +{% endhighlight %} |
| 106 | + |
| 107 | +The condition of `linkTimeIf` must be a link-time constant expression. |
| 108 | +It can only contain: |
| 109 | + |
| 110 | +* int and boolean literals and constants, |
| 111 | +* boolean operators (`!`, `&&` and `||`), |
| 112 | +* int comparison operators (such as `==` and `<`), |
| 113 | +* link-time properties in the `LinkingInfo` object, notably `esVersion`, `productionMode` and `isWebAssembly`. |
| 114 | + |
| 115 | +### Performance improvements |
| 116 | + |
| 117 | +Scala.js 1.20.1 brings a number of performance improvements compared to 1.19.0. |
| 118 | + |
| 119 | +For both JavaScript and WebAssembly: |
| 120 | + |
| 121 | +* Many low-level methods in `java.lang.Integer`, `Long`, `Float`, `Double`, `Math` and `System` have been micro-optimized. |
| 122 | +* Scala `Range`s have a faster initialization path (this particular enhancement is also coming in Scala 2.13.17 for the JVM). |
| 123 | + |
| 124 | +For JavaScript: |
| 125 | + |
| 126 | +* `Long` performance was significantly enhanced, notably additive operations, conversions to strings and conversions to floating-point numbers. |
| 127 | + |
| 128 | +For WebAssembly, performance improvements in the following areas: |
| 129 | + |
| 130 | +* Varargs, when compiled with Scala.js 1.20.0+ on Scala 2, and with the upcoming Scala 3.8.0+ on Scala 3 |
| 131 | +* `java.util.ArrayList`, `ArrayDeque`, `PriorityQueue` and `java.util.concurrent.CopyOnWriteArrayList` |
| 132 | +* `java.util.RedBlackTree.fromOrdered` |
| 133 | +* Startup time in the presence of large arrays of constants |
| 134 | +* Generally, fewer hops across the Wasm-JS boundary |
| 135 | + |
| 136 | +## Miscellaneous |
| 137 | + |
| 138 | +### New JDK APIs |
| 139 | + |
| 140 | +This release adds support for the following JDK methods: |
| 141 | + |
| 142 | +* In `java.lang.Math`: |
| 143 | + * `multiplyFull` |
| 144 | + * `multiplyHigh` |
| 145 | + * `unsignedMultiplyHigh` |
| 146 | +* In `java.lang.Double`: |
| 147 | + * `doubleToRawLongBits` |
| 148 | +* In `java.lang.Float`: |
| 149 | + * `floatToRawIntBits` |
| 150 | +* In `java.lang.Integer` and `java.lang.Long`: |
| 151 | + * `compress` |
| 152 | + * `expand` |
| 153 | + |
| 154 | +### Improved debugging experience on Wasm |
| 155 | + |
| 156 | +The Wasm backend now emits more debugging information: the names of types, object fields, local variables and global variables. |
| 157 | +This improves the debugging experience when targeting WebAssembly. |
| 158 | + |
| 159 | +## Bug fixes |
| 160 | + |
| 161 | +The following bugs have been fixed in 1.20.0 and 1.20.1: |
| 162 | + |
| 163 | +* [#5159](https://github.com/scala-js/scala-js/issues/5159) Regression in v1.19.0: `AssertionError` with module splitting on and optimizer off. |
| 164 | +* [#5165](https://github.com/scala-js/scala-js/issues/5165) Wasm: `try..finally` with non-nullable reference type produces invalid code. |
| 165 | +* [#5208](https://github.com/scala-js/scala-js/issues/5208) `doubleToLongBits` can observe non-canonical NaN bit patterns. |
| 166 | +* [#5231](https://github.com/scala-js/scala-js/issues/5231) `AssertionError`: rolled-back RuntimeLong inlining (regression in 1.20.0). |
| 167 | + |
| 168 | +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). |
0 commit comments