Announcing Scala.js 1.11.0

Sep 15, 2022.

We are excited to announce the release of Scala.js 1.11.0!

This release brings a number of bug fixes and enhancements. The highlights are:

  • Dead code elimination of unused fields, bringing code size improvements, and perhaps small performance improvements.
  • Checked exceptions for StringIndexOutOfBoundsExceptions: String.charAt(), substring(), and all their derivatives now report better errors in development mode.

Read on for more details.

Getting started

If you are new to Scala.js, head over to the tutorial.

If you need help with anything related to Scala.js, you may find our community on Gitter and on Stack Overflow.

Bug reports can be filed on GitHub.

Release notes

If upgrading from Scala.js 0.6.x, make sure to read the release notes of Scala.js 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.10.x can be used with 1.11.0 without change.
  • It is not forward binary compatible with 1.10.x: libraries compiled with 1.11.0 cannot be used with 1.10.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.10.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

Checked exceptions for StringIndexOutOfBoundsException

As documented in the semantics of Scala.js, StringIndexOutOfBoundsExceptions are Undefined Behavior in Scala.js, similarly to ClassCastExceptions and ArrayIndexOutOfBoundsExceptions. Prior to Scala.js 1.11.0, we made no effort internally to provide decent error messages, or any other kind of checks.

Starting with this release, erroneous conditions leading to a StringIndexOutOfBoundsException are checked in development (fastLink) mode. They will be reported as UndefinedBehaviorErrors in fastLink mode, and unchecked in fullLink mode.

For String.charAt, this strictly improves the error message.

For String.substring, this throws in situations that previously would, by chance, succeed. This may cause issues in your codebase if your code unknowingly relied on the undefined behavior. For code cross-compiling on the JVM, this is unlikely to happen, as the JVM would have already thrown exceptions in those cases.

Like other checked behaviors, it is now possible to configure the linker so that StringIndexOutOfBoundsExceptions are compliant. In that case, they will be thrown as specified for the JVM, in both fastLink and fullLink. You may enable them with the following sbt setting:

scalaJSLinkerConfig ~= {
  _.withSemantics(_.withStringIndexOutOfBounds(
      org.scalajs.linker.interface.CheckedBehavior.Compliant))
}

This may have significant performance impact in fullLink, like other compliant behaviors.

The sbt setting envVars is taken into account

In sbt, the envVars setting of type Map[String, String] defines environment variables to set when running and testing an application. Until now, it was ignored by Scala.js.

When using run or test, the Scala.js sbt plugin will now forward the defined variables to the configured jsEnv. Not all JSEnv implementations support environment variables. If the configured jsEnv does not support them, and envVars is non-empty, an error will be reported.

In some cases, this may cause issues with builds that relied on envVars being ignored by sbt-scalajs.

Improvements

Unused fields are dead-code-eliminated

Fields of Scala classes that are never read are now dead-code-eliminated. This can bring substantial code size improvements, although the extent of which will vary with the codebases.

As all other optimizations performed by Scala.js, this is a semantics-preserving transformation. The initial value of the field, as well as any other assignments to it, are still evaluated for their side effects. That code can only be removed if the optimizer can prove that it is pure.

Miscellaneous

New JDK APIs

  • The method java.net.URLDecoder(String, Charset) was added, to complement the existing overloads.
  • The class java.net.URLEncoder was added.

Independent scalajs-javalib.jar

Prior to Scala.js 1.11.0, the implementation of the java.* classes supported by the core were bundled inside the scalajs-library_2.x.jar artifact. This was necessary because they internally referred to features of the Scala- and Scala.js standard libraries.

Starting with Scala.js 1.11.0, our implementation of the java.* classes is self-contained. Therefore, they are extracted in a unique new artifact scalajs-javalib.jar. The various scalajs-library_2.x.jars depend on scalajs-javalib.jar, so this change should be transparent for most users.

If you have a special use case where you directly look inside scalajs-library_2.x.jar and expect to find the java.* classes there, you may have to adjust your code.

The new scalajs-javalib.jar is completely Scala-agnostic, and the Scala.js IR (stored in .sjsir files) as well. Therefore, this jar could be used by other languages targeting JavaScript from a JVM-like language without depending on Scala, if they compile to the Scala.js IR.

Bug fixes

The following bugs have been fixed in 1.11.0:

  • #4726 BigDecimal.floatValue() may be 1 ULP away from the best approximation
  • #4716 Duplicate argument name in generated JavaScript code
  • #4705 Variable named await emitted in ESModule mode

You can find the full list on GitHub.