|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: Announcing Scala.js 1.19.0 |
| 4 | +category: news |
| 5 | +tags: [releases] |
| 6 | +permalink: /news/2025/04/21/announcing-scalajs-1.19.0/ |
| 7 | +--- |
| 8 | + |
| 9 | + |
| 10 | +We are pleased to announce the release of Scala.js 1.19.0! |
| 11 | + |
| 12 | +This release comes with significant performance improvements to the WebAssembly backend. |
| 13 | +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. |
| 14 | + |
| 15 | +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. |
| 16 | +Chrome still requires a flag to enable exception handling. |
| 17 | + |
| 18 | +If you haven't tried the WebAssembly target yet, now is a good time to do so! |
| 19 | + |
| 20 | +Other highlights: |
| 21 | + |
| 22 | +* Native support for JavaScript `async/await`, through `js.async { ... }` and `js.await(...)`. |
| 23 | +* Small code size improvements for the JavaScript target when using SAM lambdas. |
| 24 | +* For Wasm only: support for the JavaScript Promise Integration feature (JSPI). |
| 25 | + |
| 26 | +Read on for more details. |
| 27 | + |
| 28 | +<!--more--> |
| 29 | + |
| 30 | +## Getting started |
| 31 | + |
| 32 | +If you are new to Scala.js, head over to [the tutorial]({{ BASE_PATH }}/tutorial/). |
| 33 | + |
| 34 | +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). |
| 35 | + |
| 36 | +Bug reports can be filed [on GitHub](https://github.com/scala-js/scala-js/issues). |
| 37 | + |
| 38 | +## Release notes |
| 39 | + |
| 40 | +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. |
| 41 | + |
| 42 | +This is a **minor** release: |
| 43 | + |
| 44 | +* 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. |
| 45 | +* 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. |
| 46 | +* 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`). |
| 47 | + |
| 48 | +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. |
| 49 | + |
| 50 | +## Enhancements with compatibility concerns |
| 51 | + |
| 52 | +### Drop support for non-strict floats |
| 53 | + |
| 54 | +Support for non-strict `Float`s was deprecated 3 years ago in Scala.js 1.9.0. |
| 55 | +We now removed that support. |
| 56 | +Builds configured to use non-strict floats (with `withStrictFloats(false)`) will refuse to compile. |
| 57 | +If that setting is used through an indirect dependency, it will be silently ignored. |
| 58 | + |
| 59 | +Strict floats are almost entirely backward compatible with non-strict floats. |
| 60 | +In general, strict floats mandate behavior which non-strict floats leave unspecified (so non-strict floats were always permitted to behave like strict floats). |
| 61 | +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`. |
| 62 | + |
| 63 | +We are not aware of any codebase that was ever adversely affected by strict float semantics. |
| 64 | + |
| 65 | +### Deprecate support for targeting ECMAScript 5.1 |
| 66 | + |
| 67 | +The support for targeting ECMAScript 5.1 is currently the biggest source of alternative code paths and polyfills in our codebase. |
| 68 | +Moreover, the ES 5.1 output does not even have exactly the same semantics as later versions: |
| 69 | + |
| 70 | +* JS classes are not true classes. Notably, that means they cannot extend native ES classes, and they do not inherit static members. |
| 71 | +* Top-level exports are declared as vars instead of lets. |
| 72 | + |
| 73 | +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. |
| 74 | +The removal will happen in a future major or minor release of Scala.js. |
| 75 | + |
| 76 | +### Changes to the IR and linker APIs |
| 77 | + |
| 78 | +For tooling authors who directly manipulate the IR and linker APIs, there have been some breaking changes in that area. |
| 79 | +This is in line with our version policy for the linker APIs. |
| 80 | + |
| 81 | +The most likely changes you may hit are: |
| 82 | + |
| 83 | +* The `ir.Trees.Closure` node has two additional fields: `flags` and `resultType`. |
| 84 | +* All the "well-known" names that were previously in `ir.Names` have been moved to a new object `ir.WellKnownNames`. |
| 85 | +* `ir.Types.PrimRef` is not a case class anymore. |
| 86 | + |
| 87 | +## Enhancements |
| 88 | + |
| 89 | +### Native support for JS `async/await` |
| 90 | + |
| 91 | +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. |
| 92 | + |
| 93 | +Note 2: when targeting WebAssembly, this feature requires support for JSPI (JavaScript Promise Integration). |
| 94 | +You can [check support for JSPI in various browsers here](https://webassembly.org/features/#table-row-jspi). |
| 95 | +On Node.js v23+, this requires the flag `--experimental-wasm-jspi`. |
| 96 | + |
| 97 | +This release adds a pair of primitives, `js.async` and `js.await`, which correspond to the `async/await` feature of JavaScript. |
| 98 | +In order to use these functions, you must configure Scala.js to target ECMAScript 2017 or later. |
| 99 | +In an sbt build, use the following setting: |
| 100 | + |
| 101 | +{% highlight scala %} |
| 102 | +// Target ES 2017 to enable async/await |
| 103 | +scalaJSLinkerConfig := { |
| 104 | + scalaJSLinkerConfig.value |
| 105 | + .withESFeatures(_.withESVersion(ESVersion.ES2017)) // enable async/await |
| 106 | +}, |
| 107 | +{% endhighlight %} |
| 108 | + |
| 109 | +You can then use `js.async { ... }` blocks containing `js.await` calls. |
| 110 | +For example: |
| 111 | + |
| 112 | +{% highlight scala %} |
| 113 | +val p: js.Promise[String] = downloadSomething() |
| 114 | +val result: js.Promise[Int] = js.async { |
| 115 | + val text: String = js.await(p) |
| 116 | + text.toInt |
| 117 | +} |
| 118 | +{% endhighlight %} |
| 119 | + |
| 120 | +`js.async { ... }` executes a block of code under a JavaScript `async` context. |
| 121 | +The block of code can await `js.Promise`s using `js.await`. |
| 122 | +Doing so will continue the execution after the call to `js.await` when the given `Promise` is resolved. |
| 123 | +If the `Promise` is rejected, the exception gets rethrown at the call site. |
| 124 | + |
| 125 | +A block such as `js.async { body }` is equivalent to an immediately-applied JavaScript `async` function: |
| 126 | + |
| 127 | +{% highlight javascript %} |
| 128 | +(async () => body)() |
| 129 | +{% endhighlight %} |
| 130 | + |
| 131 | +`js.async` returns a `js.Promise` that will be resolved with the result of the code block. |
| 132 | +If the block throws an exception, the `Promise` will be rejected. |
| 133 | + |
| 134 | +Calls to `js.await` can only appear within a `js.async` block. |
| 135 | +They must not be nested in any local method, class, by-name argument or closure. |
| 136 | +The latter includes `for` comprehensions. |
| 137 | +They may appear within conditional branches, `while` loops and `try/catch/finally` blocks. |
| 138 | + |
| 139 | +### Orphan `await`s in WebAssembly |
| 140 | + |
| 141 | +When compiling for Scala.js-on-Wasm only, you can allow calls to `js.await` anywhere, by adding the following import: |
| 142 | + |
| 143 | +{% highlight scala %} |
| 144 | +import scala.scalajs.js.wasm.JSPI.allowOrphanJSAwait |
| 145 | +{% endhighlight %} |
| 146 | + |
| 147 | +Calls to orphan `js.await`s are validated at run-time. |
| 148 | +There must exist a dynamically enclosing `js.async { ... }` block on the call stack. |
| 149 | +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`. |
| 150 | +If those conditions are not met, a JavaScript exception of type `WebAssembly.SuspendError` gets thrown. |
| 151 | + |
| 152 | +The ability to detach `js.await` calls from their corresponding `js.async` block is a new superpower offered by JSPI. |
| 153 | +It means that, as long as you enter into a `js.async` block somewhere, you can *synchronously await* `Promise`s in any arbitrary function! |
| 154 | +This is a power of the same sort as Loom on the JVM. |
| 155 | +We are looking forward to seeing new libraries built on these primitives to offer efficient and straightforward APIs. |
| 156 | + |
| 157 | +### Performance improvements for WebAssembly |
| 158 | + |
| 159 | +Scala.js 1.19.0 brings significant performance improvements to the WebAssembly output. |
| 160 | +Starting with this release, the WebAssembly output is faster than the JavaScript output on our benchmarks (geomean of the running time is 15% lower). |
| 161 | + |
| 162 | +Note that our benchmarks do not measure JavaScript interop in any significant way. |
| 163 | +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). |
| 164 | +However, if your bottleneck is in computations inside Scala code, compiling to WebAssembly should now give you a free performance boost. |
| 165 | + |
| 166 | +The WebAssembly target also received additional improvements in terms of code size and run-time memory consumption. |
| 167 | + |
| 168 | +As a reminder, you may read detailed information about [the WebAssembly backend in the docs]({{ BASE_PATH }}/doc/project/webassembly.html). |
| 169 | + |
| 170 | +## Miscellaneous |
| 171 | + |
| 172 | +### New JDK APIs |
| 173 | + |
| 174 | +This release adds support for the following JDK methods: |
| 175 | + |
| 176 | +* Most of `java.util.random.RandomGenerator` |
| 177 | + |
| 178 | +### Improvements to the JUnit interface |
| 179 | + |
| 180 | +Thanks to [`@dubinsky`](https://github.com/dubinsky) for contributing two improvements to the integration of our JUnit implementation with external tooling (in particular Gradle): |
| 181 | + |
| 182 | +* populate `sbt.testing.Event.throwable` on test failure, and |
| 183 | +* populate `sbt.testing.Event.duration`. |
| 184 | + |
| 185 | +## Bug fixes |
| 186 | + |
| 187 | +The following bugs have been fixed in 1.19.0: |
| 188 | + |
| 189 | +* [#5131](https://github.com/scala-js/scala-js/issues/5131) Linker not noticing instance test changes |
| 190 | +* [#5135](https://github.com/scala-js/scala-js/issues/5135) Deadlock in concurrent initialization of `ir.Names` and `ir.Types`. |
| 191 | + |
| 192 | +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). |
0 commit comments