diff --git a/_config.yml b/_config.yml index 0c2e717..8456fea 100644 --- a/_config.yml +++ b/_config.yml @@ -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 diff --git a/_data/library/versions.yml b/_data/library/versions.yml index 479e576..a35e67f 100644 --- a/_data/library/versions.yml +++ b/_data/library/versions.yml @@ -35,3 +35,4 @@ - 1.16.0 - 1.17.0 - 1.18.0 +- 1.19.0 diff --git a/_posts/news/2025-04-21-announcing-scalajs-1.19.0.md b/_posts/news/2025-04-21-announcing-scalajs-1.19.0.md new file mode 100644 index 0000000..e7eb360 --- /dev/null +++ b/_posts/news/2025-04-21-announcing-scalajs-1.19.0.md @@ -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. + + + +## 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). diff --git a/assets/badges/scalajs-1.19.0.svg b/assets/badges/scalajs-1.19.0.svg new file mode 100644 index 0000000..a95b45f --- /dev/null +++ b/assets/badges/scalajs-1.19.0.svg @@ -0,0 +1 @@ +scala.js: 1.19.0+scala.js1.19.0+ diff --git a/doc/all-api.md b/doc/all-api.md index f9fe4b0..867315b 100644 --- a/doc/all-api.md +++ b/doc/all-api.md @@ -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/) diff --git a/doc/internals/version-history.md b/doc/internals/version-history.md index 03204ca..1e0fb62 100644 --- a/doc/internals/version-history.md +++ b/doc/internals/version-history.md @@ -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) diff --git a/doc/project/webassembly.md b/doc/project/webassembly.md index 84161e2..49a3ebe 100644 --- a/doc/project/webassembly.md +++ b/doc/project/webassembly.md @@ -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 @@ -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 )) @@ -90,11 +91,12 @@ 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 @@ -102,17 +104,26 @@ As mentioned above, Node.js 23 and above requires the following flags: 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. diff --git a/doc/semantics.md b/doc/semantics.md index 11d7cb2..bd75c43 100644 --- a/doc/semantics.md +++ b/doc/semantics.md @@ -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 @@ -238,3 +219,24 @@ val = 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.