|
| 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 | +## Enhancements |
| 44 | + |
| 45 | +### Link-time conditional branching |
| 46 | + |
| 47 | +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. |
| 48 | +Typical examples include polyfills, as illustrated in the documentation of [`scala.scalajs.LinkingInfo`](https://javadoc.io/static/org.scala-js/scalajs-library_2.13/1.19.0/scala/scalajs/LinkingInfo$.html#esVersion:Int): |
| 49 | + |
| 50 | +{% highlight scala %} |
| 51 | +if (esVersion >= ESVersion.ES2018 || featureTest()) |
| 52 | + useES2018Feature() |
| 53 | +else |
| 54 | + usePolyfill() |
| 55 | +{% endhighlight %} |
| 56 | + |
| 57 | +which gets folded away to nothing but |
| 58 | + |
| 59 | +{% highlight scala %} |
| 60 | +useES2018Feature() |
| 61 | +{% endhighlight %} |
| 62 | + |
| 63 | +when optimizing for ES2018+. |
| 64 | + |
| 65 | +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. |
| 66 | +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: |
| 67 | + |
| 68 | +{% highlight scala %} |
| 69 | +def pow(x: Double, y: Double): Double = { |
| 70 | + if (esVersion >= ESVersion.ES2016) { |
| 71 | + (x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]) // does not link! |
| 72 | + .asInstanceOf[Double] |
| 73 | + } else { |
| 74 | + Math.pow(x, y) |
| 75 | + } |
| 76 | +} |
| 77 | +{% endhighlight %} |
| 78 | + |
| 79 | +Scala.js 1.20.x introduces `scala.scalajs.LinkingInfo.linkTimeIf`, a conditional branch that is *guaranteed* by spec to be resolved at link-time. |
| 80 | +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. |
| 81 | + |
| 82 | +{% highlight scala %} |
| 83 | +import scala.scalajs.LinkingInfo.linkTimeIf |
| 84 | + |
| 85 | +def pow(x: Double, y: Double): Double = { |
| 86 | + linkTimeIf(esVersion >= ESVersion.ES2016) { |
| 87 | + // this branch is only *linked* if we are targeting ES2016+ |
| 88 | + (x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]) |
| 89 | + .asInstanceOf[Double] |
| 90 | + } { |
| 91 | + Math.pow(x, y) |
| 92 | + } |
| 93 | +} |
| 94 | +{% endhighlight %} |
| 95 | + |
| 96 | +The condition of `linkTimeIf` must be a link-time constant expression. |
| 97 | +It can only contain: |
| 98 | + |
| 99 | +* int and boolean literals and constants, |
| 100 | +* boolean operators (`!`, `&&` and `||`), |
| 101 | +* int comparison operators (such as `==` and `<`), |
| 102 | +* link-time properties in the `LinkingInfo` object, notably `esVersion`, `productionMode` and `isWebAssembly`. |
| 103 | + |
| 104 | +### Performance improvements |
| 105 | + |
| 106 | +Scala.js 1.20.1 brings a number of performance improvements compared to 1.19.0. |
| 107 | + |
| 108 | +For both JavaScript and WebAssembly: |
| 109 | + |
| 110 | +* Many low-level methods in `java.lang.Integer`, `Long`, `Float`, `Double`, `Math` and `System` have been micro-optimized. |
| 111 | +* Scala `Range`s have a faster initialization path (this particular enhancement is also coming in Scala 2.13.17 for the JVM). |
| 112 | + |
| 113 | +For JavaScript: |
| 114 | + |
| 115 | +* `Long` performance was significantly enhanced, notably additive operations, conversions to strings and conversions to floating-point numbers. |
| 116 | + |
| 117 | +For WebAssembly, performance improvements in the following areas: |
| 118 | + |
| 119 | +* Varargs, when compiled with Scala.js 1.20.0+ on Scala 2, and with the upcoming Scala 3.8.0+ on Scala 3 |
| 120 | +* `java.util.ArrayList`, `ArrayDeque`, `PriorityQueue` and `java.util.concurrent.CopyOnWriteArrayList` |
| 121 | + |
| 122 | +## Miscellaneous |
| 123 | + |
| 124 | +### New JDK APIs |
| 125 | + |
| 126 | +This release adds support for the following JDK methods: |
| 127 | + |
| 128 | +* In `java.lang.Math`: |
| 129 | + * `multiplyFull` |
| 130 | + * `multiplyHigh` |
| 131 | + * `unsignedMultiplyHigh` |
| 132 | +* In `java.lang.Double`: |
| 133 | + * `doubleToRawLongBits` |
| 134 | +* In `java.lang.Float`: |
| 135 | + * `floatToRawIntBits` |
| 136 | +* In `java.lang.Integer` and `java.lang.Long`: |
| 137 | + * `compress` |
| 138 | + * `expand` |
| 139 | + |
| 140 | +## Bug fixes |
| 141 | + |
| 142 | +The following bugs have been fixed in 1.20.0 and 1.20.1: |
| 143 | + |
| 144 | +* [#5159](https://github.com/scala-js/scala-js/issues/5159) Regression in v1.19.0: `AssertionError` with module splitting on and optimizer off. |
| 145 | +* [#5165](https://github.com/scala-js/scala-js/issues/5165) Wasm: `try..finally` with non-nullable reference type produces invalid code. |
| 146 | +* [#5208](https://github.com/scala-js/scala-js/issues/5208) `doubleToLongBits` can observe non-canonical NaN bit patterns. |
| 147 | +* [#5231](https://github.com/scala-js/scala-js/issues/5231) `AssertionError`: rolled-back RuntimeLong inlining (regression in 1.20.0). |
| 148 | + |
| 149 | +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