Skip to content

Commit 3c3b100

Browse files
committed
Fix #4148: Do not emit static forwarders for non-top-level objects by default.
Scala/JVM does not emit static forwarders for non-top-level objects. The reason we do it in Scala.js is for implementations of JDK classes that contain static methods in inner static classes (#3950). However, this causes issues with existing Scala projects that have non-top-level `object`s and `class`es with names differing only in case. In this commit, we now only emit static forwarders for top-level objects by default, and add an opt-in option to emit them for non-top-level objects, which should be used by implementations of JDK classes.
1 parent de973d6 commit 3c3b100

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,16 +1008,22 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
10081008
* back-end considers them as colliding because they have the same name,
10091009
* but we must not.
10101010
*
1011-
* Further, unlike scalac, we emit forwarders for *any* static object, not
1012-
* just top-level ones. This is important so we can implement static methods
1011+
* By default, we only emit forwarders for top-level objects, like scalac.
1012+
* However, if requested via a compiler option, we enable them for all
1013+
* static objects. This is important so we can implement static methods
10131014
* of nested static classes of JDK APIs (see #3950).
10141015
*/
10151016

10161017
/** Is the given Scala class, interface or module class a candidate for
10171018
* static forwarders?
10181019
*/
1019-
def isCandidateForForwarders(sym: Symbol): Boolean =
1020-
!settings.noForwarders && sym.isStatic && !isImplClass(sym)
1020+
def isCandidateForForwarders(sym: Symbol): Boolean = {
1021+
!settings.noForwarders && sym.isStatic && !isImplClass(sym) && {
1022+
// Reject non-top-level objects unless opted in via the appropriate option
1023+
scalaJSOpts.genStaticForwardersForNonTopLevelObjects ||
1024+
!sym.name.containsChar('$') // this is the same test that scalac performs
1025+
}
1026+
}
10211027

10221028
/** Gen the static forwarders to the members of a class or interface for
10231029
* methods of its companion object.

compiler/src/main/scala/org/scalajs/nscplugin/ScalaJSOptions.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ trait ScalaJSOptions {
2626
* If false, bad calls to classOf will cause an error. */
2727
def fixClassOf: Boolean
2828

29+
/** Should static forwarders be emitted for non-top-level objects.
30+
*
31+
* Scala/JVM does not do that. Since Scala.js 1.2.0, we do not do it by
32+
* default either, but this option can be used to opt in. This is necessary
33+
* for implementations of JDK classes.
34+
*/
35+
def genStaticForwardersForNonTopLevelObjects: Boolean
36+
2937
/** which source locations in source maps should be relativized (or where
3038
* should they be mapped to)? */
3139
def sourceURIMaps: List[URIMap]

compiler/src/main/scala/org/scalajs/nscplugin/ScalaJSPlugin.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ScalaJSPlugin(val global: Global) extends NscPlugin {
5555
object scalaJSOpts extends ScalaJSOptions {
5656
import ScalaJSOptions.URIMap
5757
var fixClassOf: Boolean = false
58+
var genStaticForwardersForNonTopLevelObjects: Boolean = false
5859
lazy val sourceURIMaps: List[URIMap] = {
5960
if (_sourceURIMaps.nonEmpty)
6061
_sourceURIMaps.reverse
@@ -118,6 +119,8 @@ class ScalaJSPlugin(val global: Global) extends NscPlugin {
118119
for (option <- options) {
119120
if (option == "fixClassOf") {
120121
fixClassOf = true
122+
} else if (option == "genStaticForwardersForNonTopLevelObjects") {
123+
genStaticForwardersForNonTopLevelObjects = true
121124
} else if (option.startsWith("mapSourceURI:")) {
122125
val uris = option.stripPrefix("mapSourceURI:").split("->")
123126

@@ -170,6 +173,10 @@ class ScalaJSPlugin(val global: Global) extends NscPlugin {
170173
| - strips away the prefix FROM_URI (if it matches)
171174
| - optionally prefixes the TO_URI, where stripping has been performed
172175
| - any number of occurrences are allowed. Processing is done on a first match basis.
176+
| -P:$name:genStaticForwardersForNonTopLevelObjects
177+
| Generate static forwarders for non-top-level objects.
178+
| This option should be used by codebases that implement JDK classes.
179+
| When used together with -Xno-forwarders, this option has no effect.
173180
| -P:$name:fixClassOf
174181
| Repair calls to Predef.classOf that reach Scala.js.
175182
| WARNING: This is a tremendous hack! Expect ugly errors if you use this option.

project/Build.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,8 @@ object Build {
10261026
* flag prevents these mistakes from happening.
10271027
*/
10281028
scalacOptions += "-Yno-predef",
1029+
// We implement JDK classes, so we emit static forwarders for all static objects
1030+
scalacOptions += "-P:scalajs:genStaticForwardersForNonTopLevelObjects",
10291031

10301032
resourceGenerators in Compile += Def.task {
10311033
val output = (resourceManaged in Compile).value / "java/lang/Object.sjsir"
@@ -1059,6 +1061,8 @@ object Build {
10591061
* we rely on the Scala library.
10601062
*/
10611063
scalacOptions += "-Yno-predef",
1064+
// We implement JDK classes, so we emit static forwarders for all static objects
1065+
scalacOptions += "-P:scalajs:genStaticForwardersForNonTopLevelObjects",
10621066

10631067
headerSources in Compile ~= { srcs =>
10641068
srcs.filter { src =>
@@ -1706,6 +1710,8 @@ object Build {
17061710
moduleKind == ModuleKind.ESModule) // this is an approximation that works for now
17071711
},
17081712

1713+
scalacOptions in Test += "-P:scalajs:genStaticForwardersForNonTopLevelObjects",
1714+
17091715
scalaJSLinkerConfig ~= { _.withSemantics(TestSuiteLinkerOptions.semantics _) },
17101716
scalaJSModuleInitializers in Test ++= TestSuiteLinkerOptions.moduleInitializers,
17111717

0 commit comments

Comments
 (0)