aboutsummaryrefslogtreecommitdiff
path: root/sjs/backend/sjs/JSInterop.scala
blob: 6d66c32064eeb4545ddb3fe1415a054117a5f35a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package dotty.tools.backend.sjs

import dotty.tools.dotc.core._
import Contexts._
import Flags._
import Symbols._
import NameOps._
import StdNames._

import JSDefinitions._

/** Management of the interoperability with JavaScript. */
object JSInterop {

  /** Is this symbol a JavaScript type? */
  def isJSType(sym: Symbol)(implicit ctx: Context): Boolean = {
    //sym.hasAnnotation(jsdefn.RawJSTypeAnnot)
    ctx.atPhase(ctx.erasurePhase) { implicit ctx =>
      sym.derivesFrom(jsdefn.JSAnyClass)
    }
  }

  /** Is this symbol a Scala.js-defined JS class, i.e., a non-native JS class? */
  def isScalaJSDefinedJSClass(sym: Symbol)(implicit ctx: Context): Boolean =
    isJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot)

  /** Should this symbol be translated into a JS getter?
   *
   *  This is true for any parameterless method, i.e., defined without `()`.
   *  Unlike `SymDenotations.isGetter`, it applies to user-defined methods as
   *  much as *accessor* methods created for `val`s and `var`s.
   */
  def isJSGetter(sym: Symbol)(implicit ctx: Context): Boolean = {
    sym.info.firstParamTypes.isEmpty && ctx.atPhase(ctx.erasurePhase) { implicit ctx =>
      sym.info.isParameterless
    }
  }

  /** Should this symbol be translated into a JS setter?
   *
   *  This is true for any method whose name ends in `_=`.
   *  Unlike `SymDenotations.isGetter`, it applies to user-defined methods as
   *  much as *accessor* methods created for `var`s.
   */
  def isJSSetter(sym: Symbol)(implicit ctx: Context): Boolean =
    sym.name.isSetterName && sym.is(Method)

  /** Should this symbol be translated into a JS bracket access?
   *
   *  This is true for methods annotated with `@JSBracketAccess`.
   */
  def isJSBracketAccess(sym: Symbol)(implicit ctx: Context): Boolean =
    sym.hasAnnotation(jsdefn.JSBracketAccessAnnot)

  /** Should this symbol be translated into a JS bracket call?
   *
   *  This is true for methods annotated with `@JSBracketCall`.
   */
  def isJSBracketCall(sym: Symbol)(implicit ctx: Context): Boolean =
    sym.hasAnnotation(jsdefn.JSBracketCallAnnot)

  /** Is this symbol a default param accessor for a JS method?
   *
   *  For default param accessors of *constructors*, we need to test whether
   *  the companion *class* of the owner is a JS type; not whether the owner
   *  is a JS type.
   */
  def isJSDefaultParam(sym: Symbol)(implicit ctx: Context): Boolean = {
    sym.name.isDefaultGetterName && {
      val owner = sym.owner
      if (owner.is(ModuleClass) &&
          sym.name.asTermName.defaultGetterToMethod == nme.CONSTRUCTOR) {
        isJSType(owner.linkedClass)
      } else {
        isJSType(owner)
      }
    }
  }

  /** Gets the unqualified JS name of a symbol.
   *
   *  If it is not explicitly specified with an `@JSName` annotation, the
   *  JS name is inferred from the Scala name.
   */
  def jsNameOf(sym: Symbol)(implicit ctx: Context): String = {
    sym.getAnnotation(jsdefn.JSNameAnnot).flatMap(_.argumentConstant(0)).fold {
      val base = sym.name.unexpandedName.decode.toString.stripSuffix("_=")
      if (sym.is(ModuleClass)) base.stripSuffix("$")
      else if (!sym.is(Method)) base.stripSuffix(" ")
      else base
    } { constant =>
      constant.stringValue
    }
  }

  /** Gets the fully qualified JS name of a static class of module Symbol.
   *
   *  This is the JS name of the symbol qualified by the fully qualified JS
   *  name of its original owner if the latter is a native JS object.
   */
  def fullJSNameOf(sym: Symbol)(implicit ctx: Context): String = {
    assert(sym.isClass, s"fullJSNameOf called for non-class symbol $sym")
    sym.getAnnotation(jsdefn.JSFullNameAnnot).flatMap(_.argumentConstant(0)).fold {
      jsNameOf(sym)
    } { constant =>
      constant.stringValue
    }
  }

}