@@ -36,6 +36,8 @@ import org.scalajs.ir.OriginalName
3636import org .scalajs .ir .OriginalName .NoOriginalName
3737import org .scalajs .ir .Trees .OptimizerHints
3838
39+ import dotty .tools .dotc .transform .sjs .JSSymUtils ._
40+
3941import JSEncoding ._
4042import JSInterop ._
4143import ScopedVar .withScopedVars
@@ -60,6 +62,7 @@ class JSCodeGen()(using genCtx: Context) {
6062 import JSCodeGen ._
6163 import tpd ._
6264
65+ private val sjsPlatform = dotty.tools.dotc.config.SJSPlatform .sjsPlatform
6366 private val jsdefn = JSDefinitions .jsdefn
6467 private val primitives = new JSPrimitives (genCtx)
6568
@@ -461,14 +464,7 @@ class JSCodeGen()(using genCtx: Context) {
461464 val superClass =
462465 if (sym.is(Trait )) None
463466 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)
472468
473469 js.ClassDef (
474470 classIdent,
@@ -1008,6 +1004,30 @@ class JSCodeGen()(using genCtx: Context) {
10081004 result
10091005 }
10101006
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+
10111031 /** Gen JS code for a tree in expression position (in the IR) or the
10121032 * global scope.
10131033 */
@@ -2096,7 +2116,7 @@ class JSCodeGen()(using genCtx: Context) {
20962116 genApplyStatic(sym, genActualArgs(sym, args))
20972117 } else if (isJSType(sym.owner)) {
20982118 // 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 )
21002120 /* else
21012121 genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
21022122 } else {
@@ -2115,19 +2135,17 @@ class JSCodeGen()(using genCtx: Context) {
21152135 * - Getters and parameterless methods are translated as `JSBracketSelect`
21162136 * - Setters are translated to `Assign` to `JSBracketSelect`
21172137 */
2118- private def genApplyJSMethodGeneric (tree : Tree , sym : Symbol ,
2138+ private def genApplyJSMethodGeneric (sym : Symbol ,
21192139 receiver : MaybeGlobalScope , args : List [js.TreeOrJSSpread ], isStat : Boolean ,
21202140 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 = {
21242142
21252143 def noSpread = ! args.exists(_.isInstanceOf [js.JSSpread ])
21262144 val argc = args.size // meaningful only for methods that don't have varargs
21272145
21282146 def requireNotSuper (): Unit = {
21292147 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 )
21312149 }
21322150
21332151 def requireNotSpread (arg : js.TreeOrJSSpread ): js.Tree =
@@ -2156,7 +2174,7 @@ class JSCodeGen()(using genCtx: Context) {
21562174 js.JSFunctionApply (ruleOutGlobalScope(receiver), args)
21572175
21582176 case _ =>
2159- def jsFunName = js. StringLiteral (jsNameOf(sym))
2177+ def jsFunName = genExpr (jsNameOf(sym))
21602178
21612179 def genSuperReference (propName : js.Tree ): js.Tree = {
21622180 jsSuperClassValue.fold[js.Tree ] {
@@ -3479,6 +3497,84 @@ class JSCodeGen()(using genCtx: Context) {
34793497 }
34803498 }
34813499
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+
34823578 private def isMethodStaticInIR (sym : Symbol ): Boolean =
34833579 sym.is(JavaStatic )
34843580
0 commit comments