@@ -36,6 +36,8 @@ import org.scalajs.ir.OriginalName
36
36
import org .scalajs .ir .OriginalName .NoOriginalName
37
37
import org .scalajs .ir .Trees .OptimizerHints
38
38
39
+ import dotty .tools .dotc .transform .sjs .JSSymUtils ._
40
+
39
41
import JSEncoding ._
40
42
import JSInterop ._
41
43
import ScopedVar .withScopedVars
@@ -60,6 +62,7 @@ class JSCodeGen()(using genCtx: Context) {
60
62
import JSCodeGen ._
61
63
import tpd ._
62
64
65
+ private val sjsPlatform = dotty.tools.dotc.config.SJSPlatform .sjsPlatform
63
66
private val jsdefn = JSDefinitions .jsdefn
64
67
private val primitives = new JSPrimitives (genCtx)
65
68
@@ -461,14 +464,7 @@ class JSCodeGen()(using genCtx: Context) {
461
464
val superClass =
462
465
if (sym.is(Trait )) None
463
466
else Some (encodeClassNameIdent(sym.superClass))
464
- val jsNativeLoadSpec = {
465
- if (sym.is(Trait )) None
466
- else if (sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot )) None
467
- else {
468
- val path = fullJSNameOf(sym).split('.' ).toList
469
- Some (js.JSNativeLoadSpec .Global (path.head, path.tail))
470
- }
471
- }
467
+ val jsNativeLoadSpec = computeJSNativeLoadSpecOfClass(sym)
472
468
473
469
js.ClassDef (
474
470
classIdent,
@@ -1008,6 +1004,30 @@ class JSCodeGen()(using genCtx: Context) {
1008
1004
result
1009
1005
}
1010
1006
1007
+ private def genExpr (name : JSName )(implicit pos : SourcePosition ): js.Tree = name match {
1008
+ case JSName .Literal (name) => js.StringLiteral (name)
1009
+ case JSName .Computed (sym) => genComputedJSName(sym)
1010
+ }
1011
+
1012
+ private def genComputedJSName (sym : Symbol )(implicit pos : SourcePosition ): js.Tree = {
1013
+ /* By construction (i.e. restriction in PrepJSInterop), we know that sym
1014
+ * must be a static method.
1015
+ * Therefore, at this point, we can invoke it by loading its owner and
1016
+ * calling it.
1017
+ */
1018
+ def moduleOrGlobalScope = genLoadModuleOrGlobalScope(sym.owner)
1019
+ def module = genLoadModule(sym.owner)
1020
+
1021
+ if (sym.owner.isJSType) {
1022
+ if (! sym.owner.isNonNativeJSClass || sym.isJSExposed)
1023
+ genApplyJSMethodGeneric(sym, moduleOrGlobalScope, args = Nil , isStat = false )
1024
+ else
1025
+ genApplyJSClassMethod(module, sym, arguments = Nil )
1026
+ } else {
1027
+ genApplyMethod(module, sym, arguments = Nil )
1028
+ }
1029
+ }
1030
+
1011
1031
/** Gen JS code for a tree in expression position (in the IR) or the
1012
1032
* global scope.
1013
1033
*/
@@ -2096,7 +2116,7 @@ class JSCodeGen()(using genCtx: Context) {
2096
2116
genApplyStatic(sym, genActualArgs(sym, args))
2097
2117
} else if (isJSType(sym.owner)) {
2098
2118
// if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym))
2099
- genApplyJSMethodGeneric(tree, sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat)
2119
+ genApplyJSMethodGeneric(sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat)(tree.sourcePos )
2100
2120
/* else
2101
2121
genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
2102
2122
} else {
@@ -2115,19 +2135,17 @@ class JSCodeGen()(using genCtx: Context) {
2115
2135
* - Getters and parameterless methods are translated as `JSBracketSelect`
2116
2136
* - Setters are translated to `Assign` to `JSBracketSelect`
2117
2137
*/
2118
- private def genApplyJSMethodGeneric (tree : Tree , sym : Symbol ,
2138
+ private def genApplyJSMethodGeneric (sym : Symbol ,
2119
2139
receiver : MaybeGlobalScope , args : List [js.TreeOrJSSpread ], isStat : Boolean ,
2120
2140
jsSuperClassValue : Option [js.Tree ] = None )(
2121
- implicit pos : Position ): js.Tree = {
2122
-
2123
- implicit val pos : SourcePosition = tree.sourcePos
2141
+ implicit pos : SourcePosition ): js.Tree = {
2124
2142
2125
2143
def noSpread = ! args.exists(_.isInstanceOf [js.JSSpread ])
2126
2144
val argc = args.size // meaningful only for methods that don't have varargs
2127
2145
2128
2146
def requireNotSuper (): Unit = {
2129
2147
if (jsSuperClassValue.isDefined)
2130
- report.error(" Illegal super call in Scala.js-defined JS class" , tree.sourcePos )
2148
+ report.error(" Illegal super call in Scala.js-defined JS class" , pos )
2131
2149
}
2132
2150
2133
2151
def requireNotSpread (arg : js.TreeOrJSSpread ): js.Tree =
@@ -2156,7 +2174,7 @@ class JSCodeGen()(using genCtx: Context) {
2156
2174
js.JSFunctionApply (ruleOutGlobalScope(receiver), args)
2157
2175
2158
2176
case _ =>
2159
- def jsFunName = js. StringLiteral (jsNameOf(sym))
2177
+ def jsFunName = genExpr (jsNameOf(sym))
2160
2178
2161
2179
def genSuperReference (propName : js.Tree ): js.Tree = {
2162
2180
jsSuperClassValue.fold[js.Tree ] {
@@ -3479,6 +3497,84 @@ class JSCodeGen()(using genCtx: Context) {
3479
3497
}
3480
3498
}
3481
3499
3500
+ private def computeJSNativeLoadSpecOfClass (sym : Symbol ): Option [js.JSNativeLoadSpec ] = {
3501
+ if (sym.is(Trait ) || sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot )) {
3502
+ None
3503
+ } else {
3504
+ atPhase(picklerPhase.next) {
3505
+ if (sym.owner.isStaticOwner)
3506
+ Some (computeJSNativeLoadSpecOfInPhase(sym))
3507
+ else
3508
+ None
3509
+ }
3510
+ }
3511
+ }
3512
+
3513
+ private def computeJSNativeLoadSpecOfInPhase (sym : Symbol )(using Context ): js.JSNativeLoadSpec = {
3514
+ import js .JSNativeLoadSpec ._
3515
+
3516
+ val symOwner = sym.owner
3517
+
3518
+ // Marks a code path as unexpected because it should have been reported as an error in `PrepJSInterop`.
3519
+ def unexpected (msg : String ): Nothing =
3520
+ throw new FatalError (i " $msg for ${sym.fullName} at ${sym.srcPos}" )
3521
+
3522
+ if (symOwner.hasAnnotation(jsdefn.JSNativeAnnot )) {
3523
+ val jsName = sym.jsName match {
3524
+ case JSName .Literal (jsName) => jsName
3525
+ case JSName .Computed (_) => unexpected(" could not read the simple JS name as a string literal" )
3526
+ }
3527
+
3528
+ if (symOwner.hasAnnotation(jsdefn.JSGlobalScopeAnnot )) {
3529
+ Global (jsName, Nil )
3530
+ } else {
3531
+ val ownerLoadSpec = computeJSNativeLoadSpecOfInPhase(symOwner)
3532
+ ownerLoadSpec match {
3533
+ case Global (globalRef, path) =>
3534
+ Global (globalRef, path :+ jsName)
3535
+ case Import (module, path) =>
3536
+ Import (module, path :+ jsName)
3537
+ case ImportWithGlobalFallback (Import (module, modulePath), Global (globalRef, globalPath)) =>
3538
+ ImportWithGlobalFallback (
3539
+ Import (module, modulePath :+ jsName),
3540
+ Global (globalRef, globalPath :+ jsName))
3541
+ }
3542
+ }
3543
+ } else {
3544
+ def parsePath (pathName : String ): List [String ] =
3545
+ pathName.split('.' ).toList
3546
+
3547
+ def parseGlobalPath (pathName : String ): Global = {
3548
+ val globalRef :: path = parsePath(pathName)
3549
+ Global (globalRef, path)
3550
+ }
3551
+
3552
+ val annot = sym.annotations.find { annot =>
3553
+ annot.symbol == jsdefn.JSGlobalAnnot || annot.symbol == jsdefn.JSImportAnnot
3554
+ }.getOrElse {
3555
+ unexpected(" could not find the JS native load spec annotation" )
3556
+ }
3557
+
3558
+ if (annot.symbol == jsdefn.JSGlobalAnnot ) {
3559
+ val pathName = annot.argumentConstantString(0 ).getOrElse {
3560
+ sym.defaultJSName
3561
+ }
3562
+ parseGlobalPath(pathName)
3563
+ } else { // annot.symbol == jsdefn.JSImportAnnot
3564
+ val module = annot.argumentConstantString(0 ).getOrElse {
3565
+ unexpected(" could not read the module argument as a string literal" )
3566
+ }
3567
+ val path = annot.argumentConstantString(1 ).fold[List [String ]](Nil )(parsePath)
3568
+ val importSpec = Import (module, path)
3569
+ annot.argumentConstantString(2 ).fold[js.JSNativeLoadSpec ] {
3570
+ importSpec
3571
+ } { globalPathName =>
3572
+ ImportWithGlobalFallback (importSpec, parseGlobalPath(globalPathName))
3573
+ }
3574
+ }
3575
+ }
3576
+ }
3577
+
3482
3578
private def isMethodStaticInIR (sym : Symbol ): Boolean =
3483
3579
sym.is(JavaStatic )
3484
3580
0 commit comments