Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Scala.js (https://www.scala-js.org/)
*
* Copyright EPFL.
*
* Licensed under Apache License 2.0
* (https://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package org.scalajs.nscplugin
import scala.tools.nsc._
import scala.collection.mutable
import org.scalajs.ir.Trees.JSNativeLoadSpec
import org.scalajs.ir.{Trees => js}
/** Additions to Global meaningful for the JavaScript backend
*
* @author Sébastien Doeraene
*/
trait JSGlobalAddons extends JSDefinitions
with CompatComponent {
val global: Global
import global._
import jsDefinitions._
import definitions._
/** JavaScript primitives, used in jscode */
object jsPrimitives extends JSPrimitives {
val global: JSGlobalAddons.this.global.type = JSGlobalAddons.this.global
val jsAddons: ThisJSGlobalAddons =
JSGlobalAddons.this.asInstanceOf[ThisJSGlobalAddons]
}
/** Extracts the super type of a `Template`, with type parameters reinvented
* so that the type is well-formed outside of the `Template`, i.e., at the
* same level where the corresponding `ImplDef` is defined.
*/
def extractSuperTpeFromImpl(impl: Template): Type =
reinventTypeParams(impl.parents.head.tpe)
/** Reinvents all the type parameters of a `TypeRef`.
*
* This is done by existentially quantifying over all type parameters of
* the class type referenced by the `TypeRef`.
*
* As a simple example, given the definition
* {{{
* class C[A, B <: AnyVal]
* }}}
* this transforms
* {{{
* path.C[A, Int]
* }}}
* into
* {{{
* path.C[_, _ <: AnyVal]
* }}}
*
* As a complex example, given the definition
* {{{
* class D[A, B <: List[Seq[A]]]
* }}}
* this method transforms
* {{{
* path.D[?0, ?1] forSome { type ?0; type ?1 <: List[Seq[?0]] }
* }}}
*/
private def reinventTypeParams(tp: Type): Type = {
tp match {
case TypeRef(pre, sym, _) if sym.isClass && sym.typeParams.nonEmpty =>
val eparams = typeParamsToExistentials(sym)
existentialAbstraction(eparams, typeRef(pre, sym, eparams.map(_.tpe)))
case _ =>
tp
}
}
/** global javascript interop related helpers */
object jsInterop {
import scala.reflect.NameTransformer
import scala.reflect.internal.Flags
/** TopLevel exports, by owner. */
private val topLevelExports =
mutable.Map.empty[Symbol, List[TopLevelExportInfo]]
/** Static exports, by owner. */
private val staticExports =
mutable.Map.empty[Symbol, List[StaticExportInfo]]
/** JS native load specs of the symbols in the current compilation run. */
private val jsNativeLoadSpecs =
mutable.Map.empty[Symbol, JSNativeLoadSpec]
private val exportPrefix = "$js$exported$"
private val methodExportPrefix = exportPrefix + "meth$"
private val propExportPrefix = exportPrefix + "prop$"
/** Info for a non-member export. */
sealed trait ExportInfo {
val pos: Position
}
/* Not final because it causes the following compile warning:
* "The outer reference in this type test cannot be checked at run time."
*/
case class TopLevelExportInfo(moduleID: String, jsName: String)(
val pos: Position) extends ExportInfo
case class StaticExportInfo(jsName: String)(val pos: Position)
extends ExportInfo
sealed abstract class JSName {
def displayName: String
}
object JSName {
// Not final because it causes annoying compile warnings
case class Literal(name: String) extends JSName {
def displayName: String = name
}
// Not final because it causes annoying compile warnings
case class Computed(sym: Symbol) extends JSName {
def displayName: String = sym.fullName
}
}
sealed abstract class JSCallingConvention {
def displayName: String
}
object JSCallingConvention {
case object Call extends JSCallingConvention {
def displayName: String = "function application"
}
case object BracketAccess extends JSCallingConvention {
def displayName: String = "bracket access"
}
case object BracketCall extends JSCallingConvention {
def displayName: String = "bracket call"
}
case class Method(name: JSName) extends JSCallingConvention {
def displayName: String = "method '" + name.displayName + "'"
}
case class Property(name: JSName) extends JSCallingConvention {
def displayName: String = "property '" + name.displayName + "'"
}
case class UnaryOp(code: js.JSUnaryOp.Code) extends JSCallingConvention {
def displayName: String = "unary operator"
}
case class BinaryOp(code: js.JSBinaryOp.Code) extends JSCallingConvention {
def displayName: String = "binary operator"
}
def of(sym: Symbol): JSCallingConvention = {
assert(sym.isTerm, s"got non-term symbol: $sym")
if (isJSBracketAccess(sym)) {
BracketAccess
} else if (isJSBracketCall(sym)) {
BracketCall
} else {
def default = {
val jsName = jsNameOf(sym)
if (isJSProperty(sym)) Property(jsName)
else Method(jsName)
}
if (!sym.hasAnnotation(JSNameAnnotation)) {
lazy val pc = sym.paramss.map(_.size).sum
sym.name match {
case nme.apply => Call
case JSUnaryOpMethodName(code, defaultsToOp)
if (defaultsToOp || sym.hasAnnotation(JSOperatorAnnotation)) && pc == 0 =>
UnaryOp(code)
case JSBinaryOpMethodName(code, defaultsToOp)
if (defaultsToOp || sym.hasAnnotation(JSOperatorAnnotation)) && pc == 1 =>
BinaryOp(code)
case _ =>
default
}
} else {
default
}
}
}
/** Tests whether the calling convention of the specified symbol is `Call`.
*
* This helper is provided because we use this test in a few places.
*/
def isCall(sym: Symbol): Boolean =
of(sym) == Call
}
object JSUnaryOpMethodName {
private val map = Map[Name, (js.JSUnaryOp.Code, Boolean)](
nme.UNARY_+ -> (js.JSUnaryOp.+, true),
nme.UNARY_- -> (js.JSUnaryOp.-, true),
nme.UNARY_~ -> (js.JSUnaryOp.~, true),
nme.UNARY_! -> (js.JSUnaryOp.!, true)
)
/* We use Name instead of TermName to work around
* https://github.com/scala/bug/issues/11534
*/
def unapply(name: Name): Option[(js.JSUnaryOp.Code, Boolean)] =
map.get(name)
}
object JSBinaryOpMethodName {
private val map = Map[Name, (js.JSBinaryOp.Code, Boolean)](
nme.ADD -> (js.JSBinaryOp.+, true),
nme.SUB -> (js.JSBinaryOp.-, true),
nme.MUL -> (js.JSBinaryOp.*, true),
nme.DIV -> (js.JSBinaryOp./, true),
nme.MOD -> (js.JSBinaryOp.%, true),
nme.LSL -> (js.JSBinaryOp.<<, true),
nme.ASR -> (js.JSBinaryOp.>>, true),
nme.LSR -> (js.JSBinaryOp.>>>, true),
nme.OR -> (js.JSBinaryOp.|, true),
nme.AND -> (js.JSBinaryOp.&, true),
nme.XOR -> (js.JSBinaryOp.^, true),
nme.LT -> (js.JSBinaryOp.<, true),
nme.LE -> (js.JSBinaryOp.<=, true),
nme.GT -> (js.JSBinaryOp.>, true),
nme.GE -> (js.JSBinaryOp.>=, true),
nme.ZAND -> (js.JSBinaryOp.&&, true),
nme.ZOR -> (js.JSBinaryOp.||, true),
global.encode("**") -> (js.JSBinaryOp.**, false)
)
/* We use Name instead of TermName to work around
* https://github.com/scala/bug/issues/11534
*/
def unapply(name: Name): Option[(js.JSBinaryOp.Code, Boolean)] =
map.get(name)
}
def clearGlobalState(): Unit = {
topLevelExports.clear()
staticExports.clear()
jsNativeLoadSpecs.clear()
}
def registerTopLevelExports(sym: Symbol, infos: List[TopLevelExportInfo]): Unit = {
assert(!topLevelExports.contains(sym), s"symbol exported twice: $sym")
topLevelExports.put(sym, infos)
}
def registerStaticExports(sym: Symbol, infos: List[StaticExportInfo]): Unit = {
assert(!staticExports.contains(sym), s"symbol exported twice: $sym")
staticExports.put(sym, infos)
}
def topLevelExportsOf(sym: Symbol): List[TopLevelExportInfo] =
topLevelExports.getOrElse(sym, Nil)
def staticExportsOf(sym: Symbol): List[StaticExportInfo] =
staticExports.getOrElse(sym, Nil)
/** creates a name for an export specification */
def scalaExportName(jsName: String, isProp: Boolean): TermName = {
val pref = if (isProp) propExportPrefix else methodExportPrefix
val encname = NameTransformer.encode(jsName)
newTermName(pref + encname)
}
/** checks if the given symbol is a JSExport */
def isExport(sym: Symbol): Boolean =
sym.name.startsWith(exportPrefix) && !sym.hasFlag(Flags.DEFAULTPARAM)
/** retrieves the originally assigned jsName of this export and whether it
* is a property
*/
def jsExportInfo(name: Name): (String, Boolean) = {
def dropPrefix(prefix: String) ={
if (name.startsWith(prefix)) {
// We can't decode right away due to $ separators
val enc = name.toString.substring(prefix.length)
Some(NameTransformer.decode(enc))
} else None
}
dropPrefix(methodExportPrefix).map((_,false)).orElse {
dropPrefix(propExportPrefix).map((_,true))
}.getOrElse {
throw new IllegalArgumentException(
"non-exported name passed to jsExportInfo")
}
}
def jsclassAccessorFor(clazz: Symbol): Symbol =
clazz.owner.info.member(clazz.name.append("$jsclass").toTermName)
def isJSProperty(sym: Symbol): Boolean = isJSGetter(sym) || isJSSetter(sym)
@inline private def enteringUncurryIfAtPhaseAfter[A](op: => A): A = {
if (currentRun.uncurryPhase != NoPhase &&
isAtPhaseAfter(currentRun.uncurryPhase)) {
enteringPhase(currentRun.uncurryPhase)(op)
} else {
op
}
}
/** has this symbol to be translated into a JS getter (both directions)? */
def isJSGetter(sym: Symbol): Boolean = {
/* `sym.isModule` implies that `sym` is the module's accessor. In 2.12,
* module accessors are synthesized
* after uncurry, thus their first info is a MethodType at phase fields.
*/
sym.isModule || (sym.tpe.params.isEmpty && enteringUncurryIfAtPhaseAfter {
sym.tpe match {
case _: NullaryMethodType => true
case PolyType(_, _: NullaryMethodType) => true
case _ => false
}
})
}
/** has this symbol to be translated into a JS setter (both directions)? */
def isJSSetter(sym: Symbol): Boolean =
nme.isSetterName(sym.name) && sym.isMethod && !sym.isConstructor
/** has this symbol to be translated into a JS bracket access (JS to Scala) */
def isJSBracketAccess(sym: Symbol): Boolean =
sym.hasAnnotation(JSBracketAccessAnnotation)
/** has this symbol to be translated into a JS bracket call (JS to Scala) */
def isJSBracketCall(sym: Symbol): Boolean =
sym.hasAnnotation(JSBracketCallAnnotation)
/** 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): JSName = {
sym.getAnnotation(JSNameAnnotation).fold[JSName] {
JSName.Literal(defaultJSNameOf(sym))
} { annotation =>
annotation.constantAtIndex(0).collect {
case Constant(name: String) => JSName.Literal(name)
}.getOrElse {
JSName.Computed(annotation.args.head.symbol)
}
}
}
def defaultJSNameOf(sym: Symbol): String = {
val base = sym.unexpandedName.decoded.stripSuffix("_=")
if (!sym.isMethod) base.stripSuffix(" ")
else base
}
/** Stores the JS native load spec of a symbol for the current compilation
* run.
*/
def storeJSNativeLoadSpec(sym: Symbol, spec: JSNativeLoadSpec): Unit =
jsNativeLoadSpecs(sym) = spec
/** Gets the JS native load spec of a symbol in the current compilation run.
*/
def jsNativeLoadSpecOf(sym: Symbol): JSNativeLoadSpec =
jsNativeLoadSpecs(sym)
/** Gets the JS native load spec of a symbol in the current compilation run,
* if it has one.
*/
def jsNativeLoadSpecOfOption(sym: Symbol): Option[JSNativeLoadSpec] =
jsNativeLoadSpecs.get(sym)
}
}