Skip to content

Commit 66bbaf6

Browse files
authored
Merge pull request #535 from sjrd/scalajs-1.4.0
Announcing Scala.js 1.4.0.
2 parents fef52cc + 057f743 commit 66bbaf6

File tree

6 files changed

+203
-1
lines changed

6 files changed

+203
-1
lines changed

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ colors: #in hex code if not noted else
5757

5858
### VERSIONS ###
5959
versions:
60-
scalaJS: 1.3.1
60+
scalaJS: 1.4.0
6161
scalaJSBinary: 1
6262
scalaJS06x: 0.6.33
6363
scalaJS06xBinary: 0.6

_data/library/versions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@
2424
- 1.1.0
2525
- 1.2.0
2626
- 1.3.0
27+
- 1.4.0
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
---
2+
layout: post
3+
title: Announcing Scala.js 1.4.0
4+
category: news
5+
tags: [releases]
6+
permalink: /news/2021/01/12/announcing-scalajs-1.4.0/
7+
---
8+
9+
10+
We are excited to announce the release of Scala.js 1.4.0!
11+
12+
This release complements the module splitting support, introduced in Scala.js 1.3.0, with support for dynamic module loading.
13+
It is now possible to structure an application such that specific features are only loaded if necessary, the first time they are used.
14+
15+
In addition, this release significantly improves the performance of `scala.Array`.
16+
In particular, arrays of numeric types (except Longs) are implemented with JavaScript typed arrays under the hood.
17+
18+
This release also contains a number of bug fixes.
19+
The version of the Scala standard library for 2.13.x was upgraded to 2.13.4.
20+
21+
Read on for more details.
22+
23+
<!--more-->
24+
25+
## Getting started
26+
27+
If you are new to Scala.js, head over to [the tutorial]({{ BASE_PATH }}/tutorial/).
28+
29+
If you need help with anything related to Scala.js, you may find our community [on Gitter](https://gitter.im/scala-js/scala-js) and [on Stack Overflow](https://stackoverflow.com/questions/tagged/scala.js).
30+
31+
Bug reports can be filed [on GitHub](https://github.com/scala-js/scala-js/issues).
32+
33+
## Release notes
34+
35+
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.
36+
37+
This is a **minor** release:
38+
39+
* It is backward binary compatible with all earlier versions in the 1.x series: libraries compiled with 1.0.x through 1.3.x can be used with 1.4.0 without change.
40+
* It is *not* forward binary compatible with 1.3.x: libraries compiled with 1.4.0 cannot be used with 1.3.x or earlier.
41+
* It is *not* entirely backward source compatible: it is not guaranteed that a codebase will compile *as is* when upgrading from 1.3.x (in particular in the presence of `-Xfatal-warnings`).
42+
43+
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.
44+
45+
## Fixes with compatibility concerns
46+
47+
### `java.lang.Class.isAssignableFrom`
48+
49+
[`java.lang.Class.isAssignableFrom` had some serious issues](https://github.com/scala-js/scala-js/issues/4328), which we fixed in Scala.js 1.4.0.
50+
It now completly follows its specification on the JVM.
51+
However, it is possible that some code was relying on the broken behavior, which was, in some cases, closer to how `isInstance` and `isInstanceOf` works on Scala.js.
52+
53+
In particular, the following idiom, which we have seen before, is now broken more often than before:
54+
55+
{% highlight scala %}
56+
def validate[T: ClassTag](x: Any): Option[T] = {
57+
if (classTag[T].runtimeClass.isAssignableFrom(x.getClass()))
58+
Some(x.asInstanceOf[T])
59+
else
60+
None
61+
}
62+
{% endhighlight %}
63+
64+
Previously, if the `runtimeClass` was `java.lang.Integer`, and the value of `x` was the integer `5`, the above code would successfully return `Some(5)`.
65+
With the fixed `isAssignableFrom`, it will return `None` on Scala.js 1.4.0.
66+
67+
This happens because `x.getClass()` returns the smallest numeric type that can hold the value `5`, i.e., `java.lang.Byte`.
68+
The previous implementation of `isAssignableFrom` would return `true` for `classOf[j.l.Integer].isAssignableFrom(classOf[j.l.Byte])`, but it now returns `false`.
69+
70+
The above idiom can be fixed by replacing `isAssignableFrom` by `isInstance`:
71+
72+
{% highlight scala %}
73+
def validate[T: ClassTag](x: Any): Option[T] = {
74+
if (classTag[T].runtimeClass.isInstance(x))
75+
Some(x.asInstanceOf[T])
76+
else
77+
None
78+
}
79+
{% endhighlight %}
80+
81+
## Dynamic module loading
82+
83+
In Scala.js 1.3.0, we had introduced [module splitting support]({{ BASE_PATH }}/doc/project/module.html#module-splitting).
84+
Scala.js 1.4.0 goes one step further with dynamic module loading.
85+
Using this feature requires to use `fastLinkJS`/`fullLinkJS` instead of `fastOptJS`/`fullOptJS`, since it generates several .js files for one project.
86+
It is supported with any splitting mode and with any number of entry points.
87+
88+
Here is an example:
89+
90+
{% highlight scala %}
91+
class HeavyFeature {
92+
def doHeavyFeature(x: Int): Int =
93+
x * 2
94+
}
95+
96+
class MyApp {
97+
def useHeavyFeature(): Unit = {
98+
val input: Int = getInput()
99+
val resultPromise: js.Promise[Int] = js.dynamicImport {
100+
new HeavyFeature().doHeavyFeature(input)
101+
}
102+
for (result <- resultPromise.toFuture)
103+
updateUIWithOutput(result)
104+
}
105+
}
106+
{% endhighlight %}
107+
108+
The `js.dynamicImport` method has the following signature:
109+
110+
{% highlight scala %}
111+
def dynamicImport[A](body: => A): js.Promise[A]
112+
{% endhighlight %}
113+
114+
Semantically, it will evaluate `body` asynchronously and return a Promise of the result.
115+
More importantly, it acts as a border for the Scala.js linker to split out a module that will be dynamically loaded.
116+
Without going into too many details, the above Scala.js would generate something like the following JavaScript modules:
117+
118+
{% highlight javascript %}
119+
// heavyfeature.js
120+
121+
class HeavyFeature {
122+
doHeavyFeature(x) {
123+
return x * 2;
124+
}
125+
}
126+
127+
export function HeavyFeatureEntryPoint(x) {
128+
return new HeavyFeature().doHeavyFeature(x);
129+
}
130+
{% endhighlight %}
131+
132+
{% highlight javascript %}
133+
// main.js
134+
135+
class MyApp {
136+
useHeavyFeature() {
137+
const input = getInput()
138+
const resultPromise = import("./heavyfeature.js")
139+
.then(mod => mod.HeavyFeatureEntryPoint(input));
140+
resultPromise.then(result => updateUIWithOutput(result));
141+
}
142+
}
143+
{% endhighlight %}
144+
145+
In other words, the content of the `js.dynamicImport {}` block is extracted in a separate module `heavyfeature.js`, along with all its dependencies.
146+
The call is replaced by a dynamic `import()` call, followed by an invocation of the main entry point.
147+
148+
When the `main.js` application is loaded, we do not need to load, nor download, the `heavyfeature.js` file.
149+
It will only be loaded dynamically the first time we actually call `useHeavyFeature()`.
150+
This reduces initial download times for users.
151+
152+
**A word of caution:** Scala.js only splits into modules along class boundaries.
153+
Therefore, do not put the heavy feature implementation in another method of `MyApp`, that is called from the `js.dynamicImport` block.
154+
That would defeat the purpose, as the heavy feature would have to be put inside the same module as `MyApp`.
155+
Always make sure that the code called by `js.dynamicImport` bocks lives in separate classes or objects.
156+
157+
## Miscellaneous
158+
159+
### New JS APIs
160+
161+
The following APIs were added in the JS types:
162+
163+
* Added `js.RegExp.ExecResult.groups`, introduced in ECMAScript 2018 (thanks to [@vhiairrassary](https://github.com/vhiairrassary))
164+
* Added `js.TypedArray.fill`, introduced in ECMAScript 2015
165+
166+
### Tools API
167+
168+
Users of the `scalajs-ir` artifact in Scala.js itself may now use the functionality in `ir.Hashers`.
169+
Previously, trying to do so would fail to link with missing `java.security.MessageDigest`.
170+
171+
### Upgrade to GCC v20210106
172+
173+
We upgraded to the Google Closure Compiler v20210106.
174+
175+
## Bug fixes
176+
177+
Among others, the following bugs have been fixed in 1.4.0:
178+
179+
* [#4295](https://github.com/scala-js/scala-js/issues/4295) {fast,full}OptJS is still failing for empty reports in 1.3.1, with a `NoSuchElementException`.
180+
* [#4292](https://github.com/scala-js/scala-js/issues/4292) `Base64.DecodingInputStream` returns 0 instead of -1
181+
* [#4350](https://github.com/scala-js/scala-js/issues/4350) Different behavior in `Regex#replaceAllIn` w.r.t. non-matching groups
182+
* [#4328](https://github.com/scala-js/scala-js/issues/4328) `j.l.Class.isAssignableFrom` has some serious issues
183+
* [#4278](https://github.com/scala-js/scala-js/issues/4278) Local JS classes inside anonymous classes cause IR checking errors
184+
* [#4322](https://github.com/scala-js/scala-js/issues/4322) Default parameters in non-native JS traits cause IR checking errors
185+
* [#4362](https://github.com/scala-js/scala-js/issues/4362) JS constructors that require `new` cannot be called with expanded varargs (`: _*`).
186+
* [#3667](https://github.com/scala-js/scala-js/issues/3667) `RPCCore$RPCException`: encoded string too long
187+
* [#4325](https://github.com/scala-js/scala-js/issues/4325) `withPrettyPrint(true)` causes `NullPointerException` in fullOptJS
188+
* [#4212](https://github.com/scala-js/scala-js/issues/4212) Compiled JS files permissions are too restrictive
189+
190+
You can find the full list [on GitHub](https://github.com/scala-js/scala-js/issues?q=is%3Aissue+milestone%3Av1.4.0+is%3Aclosed).

assets/badges/scalajs-1.4.0.svg

Lines changed: 1 addition & 0 deletions
Loading

doc/all-api.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ title: All previous versions of the Scala.js API
55

66
## All previous versions of the API
77

8+
### Scala.js 1.4.0
9+
* [1.4.0 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.4.0/scala/scalajs/js/index.html)
10+
* [1.4.0 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.4.0/)
11+
* [1.4.0 scalajs-ir]({{ site.production_url }}/api/scalajs-ir/1.4.0/org/scalajs/ir/index.html)
12+
* [1.4.0 scalajs-linker-interface]({{ site.production_url }}/api/scalajs-linker-interface/1.4.0/org/scalajs/linker/interface/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-interface-js/1.4.0/org/scalajs/linker/interface/index.html))
13+
* [1.4.0 scalajs-linker]({{ site.production_url }}/api/scalajs-linker/1.4.0/org/scalajs/linker/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-js/1.4.0/org/scalajs/linker/index.html))
14+
* [1.4.0 scalajs-test-adapter]({{ site.production_url }}/api/scalajs-sbt-test-adapter/1.4.0/org/scalajs/testing/adapter/index.html)
15+
* [1.4.0 sbt-scalajs]({{ site.production_url }}/api/sbt-scalajs/1.4.0/#org.scalajs.sbtplugin.package)
16+
817
### Scala.js 1.3.1
918
* [1.3.1 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.3.1/scala/scalajs/js/index.html)
1019
* [1.3.1 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.3.1/)

doc/internals/version-history.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ title: Version history
55

66
## Version history of Scala.js
77

8+
- [1.4.0](/news/2021/01/12/announcing-scalajs-1.4.0/)
89
- [1.3.1](/news/2020/11/16/announcing-scalajs-1.3.1/)
910
- [1.3.0](/news/2020/10/16/announcing-scalajs-1.3.0/)
1011
- [1.2.0](/news/2020/09/09/announcing-scalajs-1.2.0/)

0 commit comments

Comments
 (0)