aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools')
-rw-r--r--src/dotty/tools/backend/sjs/GenSJSIR.scala14
-rw-r--r--src/dotty/tools/backend/sjs/JSCodeGen.scala2392
-rw-r--r--src/dotty/tools/backend/sjs/JSDefinitions.scala199
-rw-r--r--src/dotty/tools/backend/sjs/JSEncoding.scala389
-rw-r--r--src/dotty/tools/backend/sjs/JSInterop.scala110
-rw-r--r--src/dotty/tools/backend/sjs/JSPositions.scala65
-rw-r--r--src/dotty/tools/backend/sjs/JSPrimitives.scala118
-rw-r--r--src/dotty/tools/backend/sjs/ScopedVar.scala38
-rw-r--r--src/dotty/tools/dotc/Compiler.scala17
-rw-r--r--src/dotty/tools/dotc/config/PathResolver.scala24
-rw-r--r--src/dotty/tools/dotc/config/SJSPlatform.scala18
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala5
12 files changed, 13 insertions, 3376 deletions
diff --git a/src/dotty/tools/backend/sjs/GenSJSIR.scala b/src/dotty/tools/backend/sjs/GenSJSIR.scala
deleted file mode 100644
index 819a8f0e3..000000000
--- a/src/dotty/tools/backend/sjs/GenSJSIR.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-package dotty.tools.backend.sjs
-
-import dotty.tools.dotc.core._
-import Contexts._
-import Phases._
-
-/** Generates Scala.js IR files for the compilation unit. */
-class GenSJSIR extends Phase {
- def phaseName: String = "genSJSIR"
-
- def run(implicit ctx: Context): Unit = {
- new JSCodeGen().run()
- }
-}
diff --git a/src/dotty/tools/backend/sjs/JSCodeGen.scala b/src/dotty/tools/backend/sjs/JSCodeGen.scala
deleted file mode 100644
index 401e01784..000000000
--- a/src/dotty/tools/backend/sjs/JSCodeGen.scala
+++ /dev/null
@@ -1,2392 +0,0 @@
-package dotty.tools.backend.sjs
-
-import scala.annotation.switch
-
-import scala.collection.mutable
-
-import dotty.tools.FatalError
-
-import dotty.tools.dotc.CompilationUnit
-import dotty.tools.dotc.ast.tpd
-import dotty.tools.dotc.core.Phases.Phase
-
-import dotty.tools.dotc.core._
-import Periods._
-import SymDenotations._
-import Contexts._
-import Decorators._
-import Flags._
-import dotty.tools.dotc.ast.Trees._
-import Types._
-import Symbols._
-import Denotations._
-import Phases._
-import StdNames._
-
-import dotty.tools.dotc.transform.Erasure
-
-import org.scalajs.core.ir
-import org.scalajs.core.ir.{ClassKind, Position, Trees => js, Types => jstpe}
-import js.OptimizerHints
-
-import JSEncoding._
-import JSInterop._
-import ScopedVar.withScopedVars
-
-/** Main codegen for Scala.js IR.
- *
- * [[GenSJSIR]] creates one instance of `JSCodeGen` per compilation unit.
- * The `run()` method processes the whole compilation unit and generates
- * `.sjsir` files for it.
- *
- * There are 4 main levels of translation:
- *
- * - `genCompilationUnit()` iterates through all the type definitions in the
- * compilation unit. Each generated `js.ClassDef` is serialized to an
- * `.sjsir` file.
- * - `genScalaClass()` and other similar methods generate the skeleton of
- * classes.
- * - `genMethod()` and similar methods generate the declarations of methods.
- * - `genStatOrExpr()` and everything else generate the bodies of methods.
- */
-class JSCodeGen()(implicit ctx: Context) {
- import tpd._
-
- private val jsdefn = JSDefinitions.jsdefn
- private val primitives = new JSPrimitives(ctx)
-
- private val positionConversions = new JSPositions()(ctx)
- import positionConversions.{pos2irPos, implicitPos2irPos}
-
- // Some state --------------------------------------------------------------
-
- private val currentClassSym = new ScopedVar[Symbol]
- private val currentMethodSym = new ScopedVar[Symbol]
- private val localNames = new ScopedVar[LocalNameGenerator]
- private val thisLocalVarIdent = new ScopedVar[Option[js.Ident]]
- private val undefinedDefaultParams = new ScopedVar[mutable.Set[Symbol]]
-
- /** Implicitly materializes the current local name generator. */
- private implicit def implicitLocalNames: LocalNameGenerator = localNames.get
-
- /* See genSuperCall()
- * TODO Can we avoid this unscoped var?
- */
- private var isModuleInitialized: Boolean = false
-
- private def currentClassType = encodeClassType(currentClassSym)
-
- /** Returns a new fresh local identifier. */
- private def freshLocalIdent()(implicit pos: Position): js.Ident =
- localNames.get.freshLocalIdent()
-
- /** Returns a new fresh local identifier. */
- private def freshLocalIdent(base: String)(implicit pos: Position): js.Ident =
- localNames.get.freshLocalIdent(base)
-
- // Compilation unit --------------------------------------------------------
-
- def run(): Unit = {
- genCompilationUnit(ctx.compilationUnit)
- }
-
- /** Generates the Scala.js IR for a compilation unit
- * This method iterates over all the class and interface definitions
- * found in the compilation unit and emits their IR (.sjsir).
- *
- * Some classes are never actually emitted:
- * - Classes representing primitive types
- * - The scala.Array class
- *
- * TODO Some classes representing anonymous functions are not actually emitted.
- * Instead, a temporary representation of their `apply` method is built
- * and recorded, so that it can be inlined as a JavaScript anonymous
- * function in the method that instantiates it.
- *
- * Other ClassDefs are emitted according to their nature:
- * * Scala.js-defined JS class -> `genScalaJSDefinedJSClass()`
- * * Other raw JS type (<: js.Any) -> `genRawJSClassData()`
- * * Interface -> `genInterface()`
- * * Normal class -> `genClass()`
- */
- private def genCompilationUnit(cunit: CompilationUnit): Unit = {
- def collectTypeDefs(tree: Tree): List[TypeDef] = {
- tree match {
- case EmptyTree => Nil
- case PackageDef(_, stats) => stats.flatMap(collectTypeDefs)
- case cd: TypeDef => cd :: Nil
- case _: ValDef => Nil // module instance
- }
- }
- val allTypeDefs = collectTypeDefs(cunit.tpdTree)
-
- val generatedClasses = mutable.ListBuffer.empty[(Symbol, js.ClassDef)]
-
- // TODO Record anonymous JS function classes
-
- /* Finally, we emit true code for the remaining class defs. */
- for (td <- allTypeDefs) {
- val sym = td.symbol
- implicit val pos: Position = sym.pos
-
- /* Do not actually emit code for primitive types nor scala.Array. */
- val isPrimitive =
- sym.isPrimitiveValueClass || sym == defn.ArrayClass
-
- if (!isPrimitive) {
- withScopedVars(
- currentClassSym := sym
- ) {
- val tree = if (isJSType(sym)) {
- /*assert(!isRawJSFunctionDef(sym),
- s"Raw JS function def should have been recorded: $cd")*/
- if (!sym.is(Trait) && isScalaJSDefinedJSClass(sym))
- genScalaJSDefinedJSClass(td)
- else
- genRawJSClassData(td)
- } else if (sym.is(Trait)) {
- genInterface(td)
- } else {
- genScalaClass(td)
- }
-
- generatedClasses += ((sym, tree))
- }
- }
- }
-
- val clDefs = generatedClasses.map(_._2).toList
-
- for ((sym, tree) <- generatedClasses) {
- val writer = new java.io.PrintWriter(System.err)
- try {
- new ir.Printers.IRTreePrinter(writer).print(tree)
- } finally {
- writer.flush()
- }
- genIRFile(cunit, sym, tree)
- }
- }
-
- private def genIRFile(cunit: CompilationUnit, sym: Symbol,
- tree: ir.Trees.ClassDef): Unit = {
- val outfile = getFileFor(cunit, sym, ".sjsir")
- val output = outfile.bufferedOutput
- try {
- ir.InfoSerializers.serialize(output, ir.Infos.generateClassInfo(tree))
- ir.Serializers.serialize(output, tree)
- } finally {
- output.close()
- }
- }
-
- private def getFileFor(cunit: CompilationUnit, sym: Symbol,
- suffix: String) = {
- import scala.reflect.io._
-
- val outputDirectory: AbstractFile = // TODO Support virtual files
- new PlainDirectory(new Directory(new java.io.File(ctx.settings.d.value)))
-
- val pathParts = sym.fullName.toString.split("[./]")
- val dir = (outputDirectory /: pathParts.init)(_.subdirectoryNamed(_))
-
- var filename = pathParts.last
- if (sym.is(ModuleClass))
- filename = filename + nme.MODULE_SUFFIX.toString
-
- dir fileNamed (filename + suffix)
- }
-
- // Generate a class --------------------------------------------------------
-
- /** Gen the IR ClassDef for a Scala class definition (maybe a module class).
- */
- private def genScalaClass(td: TypeDef): js.ClassDef = {
- val sym = td.symbol.asClass
- implicit val pos: Position = sym.pos
-
- assert(!sym.is(Trait),
- "genScalaClass() must be called only for normal classes: "+sym)
- assert(sym.superClass != NoSymbol, sym)
-
- /*if (hasDefaultCtorArgsAndRawJSModule(sym)) {
- reporter.error(pos,
- "Implementation restriction: constructors of " +
- "Scala classes cannot have default parameters " +
- "if their companion module is JS native.")
- }*/
-
- val classIdent = encodeClassFullNameIdent(sym)
- val isHijacked = false //isHijackedBoxedClass(sym)
-
- // Optimizer hints
-
- def isStdLibClassWithAdHocInlineAnnot(sym: Symbol): Boolean = {
- val fullName = sym.fullName.toString
- (fullName.startsWith("scala.Tuple") && !fullName.endsWith("$")) ||
- (fullName.startsWith("scala.collection.mutable.ArrayOps$of"))
- }
-
- val shouldMarkInline = (
- sym.hasAnnotation(jsdefn.InlineAnnot) ||
- (sym.isAnonymousFunction && !sym.isSubClass(defn.PartialFunctionClass)) ||
- isStdLibClassWithAdHocInlineAnnot(sym))
-
- val optimizerHints = {
- OptimizerHints.empty
- .withInline(shouldMarkInline)
- .withNoinline(sym.hasAnnotation(jsdefn.NoinlineAnnot))
- }
-
- // Generate members (constructor + methods)
-
- val generatedMethods = new mutable.ListBuffer[js.MethodDef]
- val exportedSymbols = new mutable.ListBuffer[Symbol]
-
- val tpl = td.rhs.asInstanceOf[Template]
- for (tree <- tpl.constr :: tpl.body) {
- tree match {
- case EmptyTree => ()
-
- case _: ValDef =>
- () // fields are added via genClassFields()
-
- case dd: DefDef =>
- val sym = dd.symbol
-
- val isExport = false //jsInterop.isExport(sym)
- val isNamedExport = false /*isExport && sym.annotations.exists(
- _.symbol == JSExportNamedAnnotation)*/
-
- /*if (isNamedExport)
- generatedMethods += genNamedExporterDef(dd)
- else*/
- generatedMethods ++= genMethod(dd)
-
- if (isExport) {
- // We add symbols that we have to export here. This way we also
- // get inherited stuff that is implemented in this class.
- exportedSymbols += sym
- }
-
- case _ =>
- throw new FatalError("Illegal tree in body of genScalaClass(): " + tree)
- }
- }
-
- // Generate fields and add to methods + ctors
- val generatedMembers = genClassFields(td) ++ generatedMethods.toList
-
- // Generate the exported members, constructors and accessors
- val exports = {
- // Hack to export hello.world
- if (sym.fullName.toString == "hello.world$") {
- List(
- js.ModuleExportDef("hello.world"),
- js.MethodDef(static = false, js.StringLiteral("main"),
- Nil, jstpe.AnyType,
- js.Block(List(
- js.Apply(js.This()(jstpe.ClassType(classIdent.name)), js.Ident("main__V"), Nil)(jstpe.NoType),
- js.Undefined())))(
- OptimizerHints.empty, None))
- } else {
- /*
- // Generate the exported members
- val memberExports = genMemberExports(sym, exportedSymbols.toList)
-
- // Generate exported constructors or accessors
- val exportedConstructorsOrAccessors =
- if (isStaticModule(sym)) genModuleAccessorExports(sym)
- else genConstructorExports(sym)
-
- memberExports ++ exportedConstructorsOrAccessors
- */
- Nil
- }
- }
-
- // Hashed definitions of the class
- val hashedDefs =
- ir.Hashers.hashDefs(generatedMembers ++ exports)
-
- // The complete class definition
- val kind =
- if (isStaticModule(sym)) ClassKind.ModuleClass
- else if (isHijacked) ClassKind.HijackedClass
- else ClassKind.Class
-
- val classDefinition = js.ClassDef(
- classIdent,
- kind,
- Some(encodeClassFullNameIdent(sym.superClass)),
- genClassInterfaces(sym),
- None,
- hashedDefs)(
- optimizerHints)
-
- classDefinition
- }
-
- /** Gen the IR ClassDef for a Scala.js-defined JS class. */
- private def genScalaJSDefinedJSClass(td: TypeDef): js.ClassDef = {
- ???
- }
-
- /** Gen the IR ClassDef for a raw JS class or trait.
- */
- private def genRawJSClassData(td: TypeDef): js.ClassDef = {
- val sym = td.symbol.asClass
- implicit val pos: Position = sym.pos
-
- val classIdent = encodeClassFullNameIdent(sym)
- val superClass =
- if (sym.is(Trait)) None
- else Some(encodeClassFullNameIdent(sym.superClass))
- val jsName =
- if (sym.is(Trait) || sym.is(ModuleClass)) None
- else Some(fullJSNameOf(sym))
-
- js.ClassDef(classIdent, ClassKind.RawJSType,
- superClass,
- genClassInterfaces(sym),
- jsName,
- Nil)(
- OptimizerHints.empty)
- }
-
- /** Gen the IR ClassDef for an interface definition.
- */
- private def genInterface(td: TypeDef): js.ClassDef = {
- val sym = td.symbol.asClass
- implicit val pos: Position = sym.pos
-
- val classIdent = encodeClassFullNameIdent(sym)
-
- val generatedMethods = new mutable.ListBuffer[js.MethodDef]
-
- val tpl = td.rhs.asInstanceOf[Template]
- for (tree <- tpl.constr :: tpl.body) {
- tree match {
- case EmptyTree => ()
- case dd: DefDef => generatedMethods ++= genMethod(dd)
- case _ =>
- throw new FatalError("Illegal tree in gen of genInterface(): " + tree)
- }
- }
-
- val superInterfaces = genClassInterfaces(sym)
-
- // Hashed definitions of the interface
- val hashedDefs =
- ir.Hashers.hashDefs(generatedMethods.toList)
-
- js.ClassDef(classIdent, ClassKind.Interface, None, superInterfaces, None,
- hashedDefs)(OptimizerHints.empty)
- }
-
- private def genClassInterfaces(sym: ClassSymbol)(
- implicit pos: Position): List[js.Ident] = {
- import dotty.tools.dotc.transform.SymUtils._
- for {
- intf <- sym.directlyInheritedTraits
- } yield {
- encodeClassFullNameIdent(intf)
- }
- }
-
- // Generate the fields of a class ------------------------------------------
-
- /** Gen definitions for the fields of a class.
- */
- private def genClassFields(td: TypeDef): List[js.FieldDef] = {
- val classSym = td.symbol.asClass
- assert(currentClassSym.get == classSym,
- "genClassFields called with a ClassDef other than the current one")
-
- // Non-method term members are fields
- (for {
- f <- classSym.info.decls
- if !f.is(Method) && f.isTerm
- } yield {
- implicit val pos: Position = f.pos
-
- val name =
- /*if (isExposed(f)) js.StringLiteral(jsNameOf(f))
- else*/ encodeFieldSym(f)
-
- val irTpe = //if (!isScalaJSDefinedJSClass(classSym)) {
- toIRType(f.info)
- /*} else {
- val tpeEnteringPosterasure =
- enteringPhase(currentRun.posterasurePhase)(f.tpe)
- tpeEnteringPosterasure match {
- case tpe: ErasedValueType =>
- /* Here, we must store the field as the boxed representation of
- * the value class. The default value of that field, as
- * initialized at the time the instance is created, will
- * therefore be null. This will not match the behavior we would
- * get in a Scala class. To match the behavior, we would need to
- * initialized to an instance of the boxed representation, with
- * an underlying value set to the zero of its type. However we
- * cannot implement that, so we live with the discrepancy.
- * Anyway, scalac also has problems with uninitialized value
- * class values, if they come from a generic context.
- *
- * TODO Evaluate how much of this needs to be adapted for dotc,
- * which unboxes `null` to the zero of their underlying.
- */
- jstpe.ClassType(encodeClassFullName(tpe.valueClazz))
-
- case _ if f.tpe.typeSymbol == CharClass =>
- /* Will be initialized to null, which will unbox to '\0' when
- * read.
- */
- jstpe.ClassType(ir.Definitions.BoxedCharacterClass)
-
- case _ =>
- /* Other types are not boxed, so we can initialized them to
- * their true zero.
- */
- toIRType(f.tpe)
- }
- }*/
-
- js.FieldDef(name, irTpe, f.is(Mutable))
- }).toList
- }
-
- // Generate a method -------------------------------------------------------
-
- private def genMethod(dd: DefDef): Option[js.MethodDef] = {
- withScopedVars(
- localNames := new LocalNameGenerator
- ) {
- genMethodWithCurrentLocalNameScope(dd)
- }
- }
-
- /** Gen JS code for a method definition in a class or in an impl class.
- * On the JS side, method names are mangled to encode the full signature
- * of the Scala method, as described in `JSEncoding`, to support
- * overloading.
- *
- * Some methods are not emitted at all:
- * - Primitives, since they are never actually called
- * - Constructors of hijacked classes
- *
- * Constructors are emitted by generating their body as a statement.
- *
- * Other (normal) methods are emitted with `genMethodBody()`.
- */
- private def genMethodWithCurrentLocalNameScope(dd: DefDef): Option[js.MethodDef] = {
- implicit val pos: Position = dd.pos
- val sym = dd.symbol
- val vparamss = dd.vparamss
- val rhs = dd.rhs
-
- isModuleInitialized = false
-
- withScopedVars(
- currentMethodSym := sym,
- undefinedDefaultParams := mutable.Set.empty,
- thisLocalVarIdent := None
- ) {
- assert(vparamss.isEmpty || vparamss.tail.isEmpty,
- "Malformed parameter list: " + vparamss)
- val params = if (vparamss.isEmpty) Nil else vparamss.head.map(_.symbol)
-
- val isJSClassConstructor =
- sym.isClassConstructor && isScalaJSDefinedJSClass(currentClassSym)
-
- val methodName: js.PropertyName = encodeMethodSym(sym)
-
- def jsParams = for (param <- params) yield {
- implicit val pos: Position = param.pos
- js.ParamDef(encodeLocalSym(param), toIRType(param.info),
- mutable = false, rest = false)
- }
-
- /*if (primitives.isPrimitive(sym)) {
- None
- } else*/ if (sym.is(Deferred)) {
- Some(js.MethodDef(static = false, methodName,
- jsParams, toIRType(patchedResultType(sym)), js.EmptyTree)(
- OptimizerHints.empty, None))
- } else /*if (isJSNativeCtorDefaultParam(sym)) {
- None
- } else if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) {
- None
- } else*/ {
- /*def isTraitImplForwarder = dd.rhs match {
- case app: Apply => foreignIsImplClass(app.symbol.owner)
- case _ => false
- }*/
-
- val shouldMarkInline = {
- sym.hasAnnotation(jsdefn.InlineAnnot) ||
- sym.isAnonymousFunction
- }
-
- val shouldMarkNoinline = {
- sym.hasAnnotation(jsdefn.NoinlineAnnot) /*&&
- !isTraitImplForwarder*/
- }
-
- val optimizerHints = {
- OptimizerHints.empty
- .withInline(shouldMarkInline)
- .withNoinline(shouldMarkNoinline)
- }
-
- val methodDef = {
- /*if (isJSClassConstructor) {
- val body0 = genStat(rhs)
- val body1 =
- if (!sym.isPrimaryConstructor) body0
- else moveAllStatementsAfterSuperConstructorCall(body0)
- js.MethodDef(static = false, methodName,
- jsParams, jstpe.NoType, body1)(optimizerHints, None)
- } else*/ if (sym.isConstructor) {
- js.MethodDef(static = false, methodName,
- jsParams, jstpe.NoType,
- genStat(rhs))(optimizerHints, None)
- } else {
- val resultIRType = toIRType(patchedResultType(sym))
- genMethodDef(static = false, methodName,
- params, resultIRType, rhs, optimizerHints)
- }
- }
-
- Some(methodDef)
- }
- }
- }
-
- /** Generates the MethodDef of a (non-constructor) method
- *
- * Most normal methods are emitted straightforwardly. If the result
- * type is Unit, then the body is emitted as a statement. Otherwise, it is
- * emitted as an expression.
- *
- * Methods Scala.js-defined JS classes are compiled as static methods taking
- * an explicit parameter for their `this` value.
- */
- private def genMethodDef(static: Boolean, methodName: js.PropertyName,
- paramsSyms: List[Symbol], resultIRType: jstpe.Type,
- tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = {
- implicit val pos: Position = tree.pos
-
- ctx.debuglog("genMethod " + methodName.name)
- ctx.debuglog("")
-
- val jsParams = for (param <- paramsSyms) yield {
- implicit val pos: Position = param.pos
- js.ParamDef(encodeLocalSym(param), toIRType(param.info),
- mutable = false, rest = false)
- }
-
- def genBody() =
- if (resultIRType == jstpe.NoType) genStat(tree)
- else genExpr(tree)
-
- //if (!isScalaJSDefinedJSClass(currentClassSym)) {
- js.MethodDef(static, methodName, jsParams, resultIRType, genBody())(
- optimizerHints, None)
- /*} else {
- assert(!static, tree.pos)
-
- withScopedVars(
- thisLocalVarIdent := Some(freshLocalIdent("this"))
- ) {
- val thisParamDef = js.ParamDef(thisLocalVarIdent.get.get,
- jstpe.AnyType, mutable = false, rest = false)
-
- js.MethodDef(static = true, methodName, thisParamDef :: jsParams,
- resultIRType, genBody())(
- optimizerHints, None)
- }
- }*/
- }
-
- // Generate statements and expressions -------------------------------------
-
- /** Gen JS code for a tree in statement position (in the IR).
- */
- private def genStat(tree: Tree): js.Tree = {
- exprToStat(genStatOrExpr(tree, isStat = true))
- }
-
- /** Turn a JavaScript expression of type Unit into a statement */
- private def exprToStat(tree: js.Tree): js.Tree = {
- /* Any JavaScript expression is also a statement, but at least we get rid
- * of some pure expressions that come from our own codegen.
- */
- implicit val pos: Position = tree.pos
- tree match {
- case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr))
- case _:js.Literal | js.This() => js.Skip()
- case _ => tree
- }
- }
-
- /** Gen JS code for a tree in expression position (in the IR).
- */
- private def genExpr(tree: Tree): js.Tree = {
- val result = genStatOrExpr(tree, isStat = false)
- assert(result.tpe != jstpe.NoType,
- s"genExpr($tree) returned a tree with type NoType at pos ${tree.pos}")
- result
- }
-
- /** Gen JS code for a tree in statement or expression position (in the IR).
- *
- * This is the main transformation method. Each node of the Scala AST
- * is transformed into an equivalent portion of the JS AST.
- */
- private def genStatOrExpr(tree: Tree, isStat: Boolean): js.Tree = {
- implicit val pos: Position = tree.pos
-
- ctx.debuglog(" " + tree)
- ctx.debuglog("")
-
- tree match {
- /** LabelDefs (for while and do..while loops) */
- /*case lblDf: LabelDef =>
- genLabelDef(lblDf)*/
-
- /** Local val or var declaration */
- case tree @ ValDef(name, _, _) =>
- /* Must have been eliminated by the tail call transform performed
- * by genMethodBody(). */
- assert(name != nme.THIS,
- s"ValDef(_, nme.THIS, _, _) found at ${tree.pos}")
-
- val sym = tree.symbol
- val rhs = tree.rhs
- val rhsTree = genExpr(rhs)
-
- rhsTree match {
- case js.UndefinedParam() =>
- /* This is an intermediate assignment for default params on a
- * js.Any. Add the symbol to the corresponding set to inform
- * the Ident resolver how to replace it and don't emit the symbol.
- */
- undefinedDefaultParams += sym
- js.Skip()
- case _ =>
- js.VarDef(encodeLocalSym(sym),
- toIRType(sym.info), sym.is(Mutable), rhsTree)
- }
-
- case If(cond, thenp, elsep) =>
- js.If(genExpr(cond), genStatOrExpr(thenp, isStat),
- genStatOrExpr(elsep, isStat))(toIRType(tree.tpe))
-
- case Return(expr, from) =>
- // TODO Need to consider `from`?
- js.Return(toIRType(expr.tpe) match {
- case jstpe.NoType => js.Block(genStat(expr), js.Undefined())
- case _ => genExpr(expr)
- })
-
- /*case t: Try =>
- genTry(t, isStat)*/
-
- case app: Apply =>
- genApply(app, isStat)
-
- case app: TypeApply =>
- genTypeApply(app)
-
- /*case app: ApplyDynamic =>
- genApplyDynamic(app)*/
-
- case tree: This =>
- if (tree.symbol == currentClassSym.get) {
- genThis()
- } else {
- assert(tree.symbol.is(Module),
- "Trying to access the this of another class: " +
- "tree.symbol = " + tree.symbol +
- ", class symbol = " + currentClassSym.get +
- " pos:" + pos)
- genLoadModule(tree.symbol)
- }
-
- case Select(qualifier, _) =>
- val sym = tree.symbol
- if (sym.is(Module)) {
- assert(!sym.is(Package), "Cannot use package as value: " + tree)
- genLoadModule(sym)
- } else if (sym.is(JavaStatic)) {
- genLoadStaticField(sym)
- } else /*if (paramAccessorLocals contains sym) {
- paramAccessorLocals(sym).ref
- } else if (isScalaJSDefinedJSClass(sym.owner)) {
- val genQual = genExpr(qualifier)
- val boxed = if (isExposed(sym))
- js.JSBracketSelect(genQual, js.StringLiteral(jsNameOf(sym)))
- else
- js.JSDotSelect(genQual, encodeFieldSym(sym))
- fromAny(boxed,
- enteringPhase(currentRun.posterasurePhase)(sym.tpe))
- } else*/ {
- js.Select(genExpr(qualifier),
- encodeFieldSym(sym))(toIRType(sym.info))
- }
-
- case tree: Ident =>
- desugarIdent(tree).fold[js.Tree] {
- val sym = tree.symbol
- assert(!sym.is(Package), "Cannot use package as value: " + tree)
- if (sym.is(Module)) {
- genLoadModule(sym)
- } else if (undefinedDefaultParams.contains(sym)) {
- /* This is a default parameter whose assignment was moved to
- * a local variable. Put an undefined param instead.
- */
- js.UndefinedParam()(toIRType(sym.info))
- } else {
- js.VarRef(encodeLocalSym(sym))(toIRType(sym.info))
- }
- } { select =>
- genStatOrExpr(select, isStat)
- }
-
- case Literal(value) =>
- import Constants._
- value.tag match {
- case UnitTag =>
- js.Skip()
- case BooleanTag =>
- js.BooleanLiteral(value.booleanValue)
- case ByteTag | ShortTag | CharTag | IntTag =>
- js.IntLiteral(value.intValue)
- case LongTag =>
- js.LongLiteral(value.longValue)
- case FloatTag =>
- js.FloatLiteral(value.floatValue)
- case DoubleTag =>
- js.DoubleLiteral(value.doubleValue)
- case StringTag =>
- js.StringLiteral(value.stringValue)
- case NullTag =>
- js.Null()
- case ClazzTag =>
- genClassConstant(value.typeValue)
- /*case EnumTag =>
- genStaticMember(value.symbolValue)*/
- }
-
- case Block(stats, expr) =>
- js.Block(stats.map(genStat) :+ genStatOrExpr(expr, isStat))
-
- case Typed(expr, _) =>
- expr match {
- case _: Super => genThis()
- case _ => genExpr(expr)
- }
-
- case Assign(lhs0, rhs) =>
- val sym = lhs0.symbol
- if (sym.is(JavaStaticTerm))
- throw new FatalError(s"Assignment to static member ${sym.fullName} not supported")
- val genRhs = genExpr(rhs)
- val lhs = lhs0 match {
- case lhs: Ident => desugarIdent(lhs).getOrElse(lhs)
- case lhs => lhs
- }
- lhs match {
- case lhs: Select =>
- val qualifier = lhs.qualifier
-
- def ctorAssignment = (
- currentMethodSym.get.name == nme.CONSTRUCTOR &&
- currentMethodSym.get.owner == qualifier.symbol &&
- qualifier.isInstanceOf[This]
- )
- if (!sym.is(Mutable) && !ctorAssignment)
- throw new FatalError(s"Assigning to immutable field ${sym.fullName} at $pos")
-
- val genQual = genExpr(qualifier)
-
- /*if (isScalaJSDefinedJSClass(sym.owner)) {
- val genLhs = if (isExposed(sym))
- js.JSBracketSelect(genQual, js.StringLiteral(jsNameOf(sym)))
- else
- js.JSDotSelect(genQual, encodeFieldSym(sym))
- val boxedRhs =
- ensureBoxed(genRhs,
- enteringPhase(currentRun.posterasurePhase)(rhs.tpe))
- js.Assign(genLhs, boxedRhs)
- } else {*/
- js.Assign(
- js.Select(genQual, encodeFieldSym(sym))(toIRType(sym.info)),
- genRhs)
- //}
- case _ =>
- js.Assign(
- js.VarRef(encodeLocalSym(sym))(toIRType(sym.info)),
- genRhs)
- }
-
- /** Array constructor */
- case javaSeqLiteral: JavaSeqLiteral =>
- genJavaSeqLiteral(javaSeqLiteral)
-
- /** A Match reaching the backend is supposed to be optimized as a switch */
- /*case mtch: Match =>
- genMatch(mtch, isStat)*/
-
- case tree: Closure =>
- genClosure(tree)
-
- /*case EmptyTree =>
- js.Skip()*/
-
- case _ =>
- throw new FatalError("Unexpected tree in genExpr: " +
- tree + "/" + tree.getClass + " at: " + (tree.pos: Position))
- }
- } // end of genStatOrExpr()
-
- // !!! DUPLICATE code with DottyBackendInterface
- private def desugarIdent(i: Ident): Option[Select] = {
- i.tpe match {
- case TermRef(prefix: TermRef, name) =>
- Some(tpd.ref(prefix).select(i.symbol))
- case TermRef(prefix: ThisType, name) =>
- Some(tpd.This(prefix.cls).select(i.symbol))
- /*case TermRef(NoPrefix, name) =>
- if (i.symbol is Method) Some(This(i.symbol.topLevelClass).select(i.symbol)) // workaround #342 todo: remove after fixed
- else None*/
- case _ =>
- None
- }
- }
-
- private def qualifierOf(fun: Tree): Tree = fun match {
- case fun: Ident =>
- fun.tpe match {
- case TermRef(prefix: TermRef, _) => tpd.ref(prefix)
- case TermRef(prefix: ThisType, _) => tpd.This(prefix.cls)
- }
- case Select(qualifier, _) =>
- qualifier
- case TypeApply(fun, _) =>
- qualifierOf(fun)
- }
-
- /** Gen JS this of the current class.
- * Normally encoded straightforwardly as a JS this.
- * But must be replaced by the `thisLocalVarIdent` local variable if there
- * is one.
- */
- private def genThis()(implicit pos: Position): js.Tree = {
- /*if (tryingToGenMethodAsJSFunction) {
- throw new CancelGenMethodAsJSFunction(
- "Trying to generate `this` inside the body")
- }*/
-
- thisLocalVarIdent.fold[js.Tree] {
- js.This()(currentClassType)
- } { thisLocalIdent =>
- js.VarRef(thisLocalIdent)(currentClassType)
- }
- }
-
- /** Gen JS code for an Apply node (method call)
- *
- * There's a whole bunch of varieties of Apply nodes: regular method
- * calls, super calls, constructor calls, isInstanceOf/asInstanceOf,
- * primitives, JS calls, etc. They are further dispatched in here.
- */
- private def genApply(tree: Apply, isStat: Boolean): js.Tree = {
- implicit val pos: Position = tree.pos
- val args = tree.args
- val sym = tree.fun.symbol
-
- val fun = tree.fun match {
- case fun: Ident => desugarIdent(fun).getOrElse(fun)
- case fun => fun
- }
-
- fun match {
- case _ if isJSDefaultParam(sym) =>
- js.UndefinedParam()(toIRType(sym.info.finalResultType))
-
- case Select(Super(_, _), _) =>
- genSuperCall(tree, isStat)
-
- case Select(New(_), nme.CONSTRUCTOR) =>
- genApplyNew(tree)
-
- case _ =>
- /*if (sym.isLabel) {
- genLabelApply(tree)
- } else*/ if (primitives.isPrimitive(tree)) {
- genPrimitiveOp(tree, isStat)
- } else if (Erasure.Boxing.isBox(sym)) {
- // Box a primitive value (cannot be Unit)
- val arg = args.head
- makePrimitiveBox(genExpr(arg), arg.tpe)
- } else if (Erasure.Boxing.isUnbox(sym)) {
- // Unbox a primitive value (cannot be Unit)
- val arg = args.head
- makePrimitiveUnbox(genExpr(arg), tree.tpe)
- } else {
- genNormalApply(tree, isStat)
- }
- }
- }
-
- /** Gen JS code for a super call, of the form Class.super[mix].fun(args).
- *
- * This does not include calls defined in mixin traits, as these are
- * already desugared by the 'mixin' phase. Only calls to super classes
- * remain.
- *
- * Since a class has exactly one direct superclass, and calling a method
- * two classes above the current one is invalid in Scala, the `mix` item is
- * irrelevant.
- */
- private def genSuperCall(tree: Apply, isStat: Boolean): js.Tree = {
- implicit val pos: Position = tree.pos
- val Apply(fun @ Select(sup @ Super(_, mix), _), args) = tree
- val sym = fun.symbol
-
- if (sym == defn.Any_getClass) {
- // The only primitive that is also callable as super call
- js.GetClass(genThis())
- } else /*if (isScalaJSDefinedJSClass(currentClassSym)) {
- genJSSuperCall(tree, isStat)
- } else*/ {
- val superCall = genApplyMethodStatically(
- genThis()(sup.pos), sym, genActualArgs(sym, args))
-
- // Initialize the module instance just after the super constructor call.
- if (isStaticModule(currentClassSym) && !isModuleInitialized &&
- currentMethodSym.get.isClassConstructor) {
- isModuleInitialized = true
- val thisType = jstpe.ClassType(encodeClassFullName(currentClassSym))
- val initModule = js.StoreModule(thisType, js.This()(thisType))
- js.Block(superCall, initModule)
- } else {
- superCall
- }
- }
- }
-
- /** Gen JS code for a constructor call (new).
- * Further refined into:
- * * new String(...)
- * * new of a hijacked boxed class
- * * new of an anonymous function class that was recorded as JS function
- * * new of a raw JS class
- * * new Array
- * * regular new
- */
- private def genApplyNew(tree: Apply): js.Tree = {
- implicit val pos: Position = tree.pos
-
- val Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) = tree
- val ctor = fun.symbol
- val tpe = tpt.tpe
-
- assert(ctor.isClassConstructor,
- "'new' call to non-constructor: " + ctor.name)
-
- if (tpe.isRef(defn.StringClass)) {
- genNewString(ctor, genActualArgs(ctor, args))
- } else /*if (isHijackedBoxedClass(tpe.typeSymbol)) {
- genNewHijackedBoxedClass(tpe.typeSymbol, ctor, args map genExpr)
- } else if (translatedAnonFunctions contains tpe.typeSymbol) {
- val functionMaker = translatedAnonFunctions(tpe.typeSymbol)
- functionMaker(args map genExpr)
- } else*/ if (isJSType(tpe.widenDealias.typeSymbol)) {
- val clsSym = tpe.widenDealias.typeSymbol
- if (clsSym == jsdefn.JSObjectClass && args.isEmpty) js.JSObjectConstr(Nil)
- else if (clsSym == jsdefn.JSArrayClass && args.isEmpty) js.JSArrayConstr(Nil)
- else js.JSNew(genLoadJSConstructor(clsSym), genActualJSArgs(ctor, args))
- } else {
- toIRType(tpe) match {
- case cls: jstpe.ClassType =>
- js.New(cls, encodeMethodSym(ctor), genActualArgs(ctor, args))
-
- case other =>
- throw new FatalError(s"Non ClassType cannot be instantiated: $other")
- }
- }
- }
-
- /** Gen JS code for a primitive method call. */
- private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = {
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- implicit val pos: Position = tree.pos
-
- val Apply(fun, args) = tree
- val receiver = qualifierOf(fun)
-
- val code = primitives.getPrimitive(tree, receiver.tpe)
-
- if (isArithmeticOp(code) || isLogicalOp(code) || isComparisonOp(code))
- genSimpleOp(tree, receiver :: args, code)
- else if (code == CONCAT)
- genStringConcat(tree, receiver, args)
- else if (code == HASH)
- genScalaHash(tree, receiver)
- else if (isArrayOp(code))
- genArrayOp(tree, code)
- else if (code == SYNCHRONIZED)
- genSynchronized(tree, isStat)
- else if (isCoercion(code))
- genCoercion(tree, receiver, code)
- else if (code == JSPrimitives.THROW)
- genThrow(tree, args)
- else /*if (primitives.isJSPrimitive(code))
- genJSPrimitive(tree, receiver, args, code)
- else*/
- throw new FatalError(s"Unknown primitive: ${tree.symbol.fullName} at: $pos")
- }
-
- /** Gen JS code for a simple operation (arithmetic, logical, or comparison) */
- private def genSimpleOp(tree: Apply, args: List[Tree], code: Int): js.Tree = {
- args match {
- case List(arg) => genSimpleUnaryOp(tree, arg, code)
- case List(lhs, rhs) => genSimpleBinaryOp(tree, lhs, rhs, code)
- case _ => throw new FatalError("Incorrect arity for primitive")
- }
- }
-
- /** Gen JS code for a simple unary operation. */
- private def genSimpleUnaryOp(tree: Apply, arg: Tree, code: Int): js.Tree = {
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- implicit val pos: Position = tree.pos
-
- val genArg = genExpr(arg)
- val resultIRType = toIRType(tree.tpe)
-
- (code: @switch) match {
- case POS =>
- genArg
-
- case NEG =>
- (resultIRType: @unchecked) match {
- case jstpe.IntType =>
- js.BinaryOp(js.BinaryOp.Int_-, js.IntLiteral(0), genArg)
- case jstpe.LongType =>
- js.BinaryOp(js.BinaryOp.Long_-, js.LongLiteral(0), genArg)
- case jstpe.FloatType =>
- js.BinaryOp(js.BinaryOp.Float_-, js.FloatLiteral(0.0f), genArg)
- case jstpe.DoubleType =>
- js.BinaryOp(js.BinaryOp.Double_-, js.DoubleLiteral(0), genArg)
- }
-
- case NOT =>
- (resultIRType: @unchecked) match {
- case jstpe.IntType =>
- js.BinaryOp(js.BinaryOp.Int_^, js.IntLiteral(-1), genArg)
- case jstpe.LongType =>
- js.BinaryOp(js.BinaryOp.Long_^, js.LongLiteral(-1), genArg)
- }
-
- case ZNOT =>
- js.UnaryOp(js.UnaryOp.Boolean_!, genArg)
-
- case _ =>
- throw new FatalError("Unknown unary operation code: " + code)
- }
- }
-
- /** Gen JS code for a simple binary operation. */
- private def genSimpleBinaryOp(tree: Apply, lhs: Tree, rhs: Tree, code: Int): js.Tree = {
- import scala.tools.nsc.backend.ScalaPrimitives._
- import js.UnaryOp._
-
- /* Codes for operation types, in an object so that they can be 'final val'
- * and be used in switch-matches.
- */
- object OpTypes {
- final val DoubleOp = 1
- final val FloatOp = 2
- final val LongOp = 3
- final val IntOp = 4
- final val BooleanOp = 5
- final val AnyOp = 6
- }
- import OpTypes._
-
- implicit val pos: Position = tree.pos
-
- val lhsIRType = toIRType(lhs.tpe)
- val rhsIRType = toIRType(rhs.tpe)
-
- val opType = (lhsIRType, rhsIRType) match {
- case (jstpe.DoubleType, _) | (_, jstpe.DoubleType) => DoubleOp
- case (jstpe.FloatType, _) | (_, jstpe.FloatType) => FloatOp
- case (jstpe.LongType, _) | (_, jstpe.LongType) => LongOp
- case (jstpe.IntType, _) | (_, jstpe.IntType) => IntOp
- case (jstpe.BooleanType, jstpe.BooleanType) => BooleanOp
- case _ => AnyOp
- }
-
- if (opType == AnyOp && isUniversalEqualityOp(code)) {
- genUniversalEqualityOp(lhs, rhs, code)
- } else if (code == ZOR) {
- js.If(genExpr(lhs), js.BooleanLiteral(true), genExpr(rhs))(jstpe.BooleanType)
- } else if (code == ZAND) {
- js.If(genExpr(lhs), genExpr(rhs), js.BooleanLiteral(false))(jstpe.BooleanType)
- } else {
- import js.BinaryOp._
-
- def coerce(tree: js.Tree, opType: Int): js.Tree = (opType: @switch) match {
- case DoubleOp =>
- if (tree.tpe == jstpe.LongType) js.UnaryOp(LongToDouble, tree)
- else tree
-
- case FloatOp =>
- if (tree.tpe == jstpe.FloatType || tree.tpe == jstpe.IntType) tree
- else js.UnaryOp(DoubleToFloat, coerce(tree, DoubleOp))
-
- case LongOp =>
- if (tree.tpe == jstpe.LongType) tree
- else {
- assert(tree.tpe == jstpe.IntType)
- js.UnaryOp(IntToLong, tree)
- }
-
- case IntOp =>
- if (tree.tpe == jstpe.IntType) tree
- else {
- assert(tree.tpe == jstpe.LongType)
- js.UnaryOp(LongToInt, tree)
- }
-
- case BooleanOp | AnyOp =>
- tree
- }
-
- val rhsOpType = code match {
- case LSL | LSR | ASR => IntOp
- case _ => opType
- }
-
- val genLhs = coerce(genExpr(lhs), opType)
- val genRhs = coerce(genExpr(rhs), rhsOpType)
-
- val op = (opType: @switch) match {
- case IntOp =>
- (code: @switch) match {
- case ADD => Int_+
- case SUB => Int_-
- case MUL => Int_*
- case DIV => Int_/
- case MOD => Int_%
- case OR => Int_|
- case AND => Int_&
- case XOR => Int_^
- case LSL => Int_<<
- case LSR => Int_>>>
- case ASR => Int_>>
-
- case EQ => Num_==
- case NE => Num_!=
- case LT => Num_<
- case LE => Num_<=
- case GT => Num_>
- case GE => Num_>=
- }
-
- case FloatOp =>
- (code: @switch) match {
- case ADD => Float_+
- case SUB => Float_-
- case MUL => Float_*
- case DIV => Float_/
- case MOD => Float_%
-
- case EQ => Num_==
- case NE => Num_!=
- case LT => Num_<
- case LE => Num_<=
- case GT => Num_>
- case GE => Num_>=
- }
-
- case DoubleOp =>
- (code: @switch) match {
- case ADD => Double_+
- case SUB => Double_-
- case MUL => Double_*
- case DIV => Double_/
- case MOD => Double_%
-
- case EQ => Num_==
- case NE => Num_!=
- case LT => Num_<
- case LE => Num_<=
- case GT => Num_>
- case GE => Num_>=
- }
-
- case LongOp =>
- (code: @switch) match {
- case ADD => Long_+
- case SUB => Long_-
- case MUL => Long_*
- case DIV => Long_/
- case MOD => Long_%
- case OR => Long_|
- case XOR => Long_^
- case AND => Long_&
- case LSL => Long_<<
- case LSR => Long_>>>
- case ASR => Long_>>
-
- case EQ => Long_==
- case NE => Long_!=
- case LT => Long_<
- case LE => Long_<=
- case GT => Long_>
- case GE => Long_>=
- }
-
- case BooleanOp =>
- (code: @switch) match {
- case EQ => Boolean_==
- case NE => Boolean_!=
- case OR => Boolean_|
- case AND => Boolean_&
- case XOR => Boolean_!=
- }
-
- case AnyOp =>
- /* No @switch because some 2.11 version erroneously report a warning
- * for switches with less than 3 non-default cases.
- */
- code match {
- case ID => ===
- case NI => !==
- }
- }
-
- js.BinaryOp(op, genLhs, genRhs)
- }
- }
-
- /** Gen JS code for a universal equality test. */
- private def genUniversalEqualityOp(lhs: Tree, rhs: Tree, code: Int)(
- implicit pos: Position): js.Tree = {
-
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- val genLhs = genExpr(lhs)
- val genRhs = genExpr(rhs)
-
- val bypassEqEq = {
- // Do not call equals if we have a literal null at either side.
- genLhs.isInstanceOf[js.Null] ||
- genRhs.isInstanceOf[js.Null]
- }
-
- if (bypassEqEq) {
- js.BinaryOp(
- if (code == EQ) js.BinaryOp.=== else js.BinaryOp.!==,
- genLhs, genRhs)
- } else {
- val body = genEqEqPrimitive(lhs.tpe, rhs.tpe, genLhs, genRhs)
- if (code == EQ) body
- else js.UnaryOp(js.UnaryOp.Boolean_!, body)
- }
- }
-
- private lazy val externalEqualsNumNum: Symbol =
- defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum)
- private lazy val externalEqualsNumChar: Symbol =
- NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private
- private lazy val externalEqualsNumObject: Symbol =
- defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject)
- private lazy val externalEquals: Symbol =
- defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol
-
- /** Gen JS code for a call to Any.== */
- private def genEqEqPrimitive(ltpe: Type, rtpe: Type, lsrc: js.Tree, rsrc: js.Tree)(
- implicit pos: Position): js.Tree = {
- ctx.debuglog(s"$ltpe == $rtpe")
- val lsym = ltpe.widenDealias.typeSymbol.asClass
- val rsym = rtpe.widenDealias.typeSymbol.asClass
-
- /* True if the equality comparison is between values that require the
- * use of the rich equality comparator
- * (scala.runtime.BoxesRunTime.equals).
- * This is the case when either side of the comparison might have a
- * run-time type subtype of java.lang.Number or java.lang.Character,
- * **which includes when either is a JS type**.
- * When it is statically known that both sides are equal and subtypes of
- * Number or Character, not using the rich equality is possible (their
- * own equals method will do ok.)
- */
- val mustUseAnyComparator: Boolean = {
- isJSType(lsym) || isJSType(rsym) || {
- val p = ctx.platform
- val areSameFinals = lsym.is(Final) && rsym.is(Final) && (ltpe =:= rtpe)
- !areSameFinals && p.isMaybeBoxed(lsym) && p.isMaybeBoxed(rsym)
- }
- }
-
- if (mustUseAnyComparator) {
- val equalsMethod: Symbol = {
- // scalastyle:off line.size.limit
- val ptfm = ctx.platform
- if (lsym.derivesFrom(defn.BoxedNumberClass)) {
- if (rsym.derivesFrom(defn.BoxedNumberClass)) externalEqualsNumNum
- else if (rsym.derivesFrom(defn.BoxedCharClass)) externalEqualsNumObject // will be externalEqualsNumChar in 2.12, SI-9030
- else externalEqualsNumObject
- } else externalEquals
- // scalastyle:on line.size.limit
- }
- genModuleApplyMethod(equalsMethod, List(lsrc, rsrc))
- } else {
- // if (lsrc eq null) rsrc eq null else lsrc.equals(rsrc)
- if (lsym == defn.StringClass) {
- // String.equals(that) === (this eq that)
- js.BinaryOp(js.BinaryOp.===, lsrc, rsrc)
- } else {
- /* This requires to evaluate both operands in local values first.
- * The optimizer will eliminate them if possible.
- */
- val ltemp = js.VarDef(freshLocalIdent(), lsrc.tpe, mutable = false, lsrc)
- val rtemp = js.VarDef(freshLocalIdent(), rsrc.tpe, mutable = false, rsrc)
- js.Block(
- ltemp,
- rtemp,
- js.If(js.BinaryOp(js.BinaryOp.===, ltemp.ref, js.Null()),
- js.BinaryOp(js.BinaryOp.===, rtemp.ref, js.Null()),
- genApplyMethod(ltemp.ref, defn.Any_equals, List(rtemp.ref)))(
- jstpe.BooleanType))
- }
- }
- }
-
- /** Gen JS code for string concatenation.
- */
- private def genStringConcat(tree: Apply, receiver: Tree,
- args: List[Tree]): js.Tree = {
- implicit val pos: Position = tree.pos
-
- val arg = args.head
-
- /* Primitive number types such as scala.Int have a
- * def +(s: String): String
- * method, which is why we have to box the lhs sometimes.
- * Otherwise, both lhs and rhs are already reference types (Any or String)
- * so boxing is not necessary (in particular, rhs is never a primitive).
- */
- assert(!isPrimitiveValueType(receiver.tpe) || arg.tpe.isRef(defn.StringClass))
- assert(!isPrimitiveValueType(arg.tpe))
-
- val genLhs = {
- val genLhs0 = genExpr(receiver)
- // Box the receiver if it is a primitive value
- if (!isPrimitiveValueType(receiver.tpe)) genLhs0
- else makePrimitiveBox(genLhs0, receiver.tpe)
- }
-
- val genRhs = genExpr(arg)
-
- js.BinaryOp(js.BinaryOp.String_+, genLhs, genRhs)
- }
-
- /** Gen JS code for a call to Any.## */
- private def genScalaHash(tree: Apply, receiver: Tree): js.Tree = {
- implicit val pos: Position = tree.pos
-
- genModuleApplyMethod(defn.ScalaRuntimeModule.requiredMethod(nme.hash_),
- List(genExpr(receiver)))
- }
-
- /** Gen JS code for an array operation (get, set or length) */
- private def genArrayOp(tree: Tree, code: Int): js.Tree = {
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- implicit val pos: Position = tree.pos
-
- val Apply(fun, args) = tree
- val arrayObj = qualifierOf(fun)
-
- val genArray = genExpr(arrayObj)
- val genArgs = args.map(genExpr)
-
- def elementType: Type = arrayObj.tpe.widenDealias match {
- case defn.ArrayOf(el) => el
- case JavaArrayType(el) => el
- case tpe =>
- ctx.error(s"expected Array $tpe")
- ErrorType
- }
-
- def genSelect(): js.Tree =
- js.ArraySelect(genArray, genArgs(0))(toIRType(elementType))
-
- if (isArrayGet(code)) {
- // get an item of the array
- assert(args.length == 1,
- s"Array get requires 1 argument, found ${args.length} in $tree")
- genSelect()
- } else if (isArraySet(code)) {
- // set an item of the array
- assert(args.length == 2,
- s"Array set requires 2 arguments, found ${args.length} in $tree")
- js.Assign(genSelect(), genArgs(1))
- } else {
- // length of the array
- js.ArrayLength(genArray)
- }
- }
-
- /** Gen JS code for a call to AnyRef.synchronized */
- private def genSynchronized(tree: Apply, isStat: Boolean): js.Tree = {
- /* JavaScript is single-threaded, so we can drop the
- * synchronization altogether.
- */
- val Apply(fun, List(arg)) = tree
- val receiver = qualifierOf(fun)
-
- val genReceiver = genExpr(receiver)
- val genArg = genStatOrExpr(arg, isStat)
-
- genReceiver match {
- case js.This() =>
- // common case for which there is no side-effect nor NPE
- genArg
- case _ =>
- implicit val pos: Position = tree.pos
- /* TODO Check for a null receiver?
- * In theory, it's UB, but that decision should be left for link time.
- */
- js.Block(genReceiver, genArg)
- }
- }
-
- /** Gen JS code for a coercion */
- private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = {
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- implicit val pos: Position = tree.pos
-
- val source = genExpr(receiver)
-
- def source2int = (code: @switch) match {
- case F2C | D2C | F2B | D2B | F2S | D2S | F2I | D2I =>
- js.UnaryOp(js.UnaryOp.DoubleToInt, source)
- case L2C | L2B | L2S | L2I =>
- js.UnaryOp(js.UnaryOp.LongToInt, source)
- case _ =>
- source
- }
-
- (code: @switch) match {
- // To Char, need to crop at unsigned 16-bit
- case B2C | S2C | I2C | L2C | F2C | D2C =>
- js.BinaryOp(js.BinaryOp.Int_&, source2int, js.IntLiteral(0xffff))
-
- // To Byte, need to crop at signed 8-bit
- case C2B | S2B | I2B | L2B | F2B | D2B =>
- // note: & 0xff would not work because of negative values
- js.BinaryOp(js.BinaryOp.Int_>>,
- js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(24)),
- js.IntLiteral(24))
-
- // To Short, need to crop at signed 16-bit
- case C2S | I2S | L2S | F2S | D2S =>
- // note: & 0xffff would not work because of negative values
- js.BinaryOp(js.BinaryOp.Int_>>,
- js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(16)),
- js.IntLiteral(16))
-
- // To Int, need to crop at signed 32-bit
- case L2I | F2I | D2I =>
- source2int
-
- // Any int to Long
- case C2L | B2L | S2L | I2L =>
- js.UnaryOp(js.UnaryOp.IntToLong, source)
-
- // Any double to Long
- case F2L | D2L =>
- js.UnaryOp(js.UnaryOp.DoubleToLong, source)
-
- // Long to Double
- case L2D =>
- js.UnaryOp(js.UnaryOp.LongToDouble, source)
-
- // Any int, or Double, to Float
- case C2F | B2F | S2F | I2F | D2F =>
- js.UnaryOp(js.UnaryOp.DoubleToFloat, source)
-
- // Long to Float === Long to Double to Float
- case L2F =>
- js.UnaryOp(js.UnaryOp.DoubleToFloat,
- js.UnaryOp(js.UnaryOp.LongToDouble, source))
-
- // Identities and IR upcasts
- case C2C | B2B | S2S | I2I | L2L | F2F | D2D |
- C2I | C2D |
- B2S | B2I | B2D |
- S2I | S2D |
- I2D |
- F2D =>
- source
- }
- }
-
- /** Gen a call to the special `throw` method. */
- private def genThrow(tree: Apply, args: List[Tree]): js.Tree = {
- implicit val pos: Position = tree.pos
- val exception = args.head
- val genException = genExpr(exception)
- js.Throw {
- if (exception.tpe.widenDealias.typeSymbol.derivesFrom(jsdefn.JavaScriptExceptionClass)) {
- genModuleApplyMethod(
- jsdefn.RuntimePackage_unwrapJavaScriptException,
- List(genException))
- } else {
- genException
- }
- }
- }
-
- /** Gen a "normal" apply (to a true method).
- *
- * But even these are further refined into:
- * * Methods of java.lang.String, which are redirected to the
- * RuntimeString trait implementation.
- * * Calls to methods of raw JS types (Scala.js -> JS interop)
- * * Calls to methods in impl classes of Scala2 traits.
- * * Regular method call
- */
- private def genNormalApply(tree: Apply, isStat: Boolean): js.Tree = {
- implicit val pos: Position = tree.pos
-
- val fun = tree.fun match {
- case fun: Ident => desugarIdent(fun).get
- case fun: Select => fun
- }
- val receiver = fun.qualifier
- val args = tree.args
- val sym = fun.symbol
-
- def isStringMethodFromObject: Boolean = sym.name match {
- case nme.toString_ | nme.equals_ | nme.hashCode_ => true
- case _ => false
- }
-
- if (sym.owner == defn.StringClass && !isStringMethodFromObject) {
- genApplyMethodOfString(genExpr(receiver), sym, genActualArgs(sym, args))
- } else if (isJSType(sym.owner)) {
- //if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym))
- genApplyJSMethodGeneric(tree, sym, genExpr(receiver), genActualJSArgs(sym, args), isStat)
- /*else
- genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
- } else if (foreignIsImplClass(sym.owner)) {
- genTraitImplApply(sym, args.map(genExpr))
- } else if (sym.isClassConstructor) {
- // Calls to constructors are always statically linked
- genApplyMethodStatically(genExpr(receiver), sym, genActualArgs(sym, args))
- } else {
- genApplyMethod(genExpr(receiver), sym, genActualArgs(sym, args))
- }
- }
-
- /** Gen JS code for a call to a JS method (of a subclass of `js.Any`).
- *
- * Basically it boils down to calling the method as a `JSBracketSelect`,
- * without name mangling. But other aspects come into play:
- *
- * - Operator methods are translated to JS operators (not method calls)
- * - `apply` is translated as a function call, i.e., `o()` instead of `o.apply()`
- * - Scala varargs are turned into JS varargs (see `genPrimitiveJSArgs()`)
- * - Getters and parameterless methods are translated as `JSBracketSelect`
- * - Setters are translated to `Assign` to `JSBracketSelect`
- */
- private def genApplyJSMethodGeneric(tree: Tree, sym: Symbol,
- receiver: js.Tree, args: List[js.Tree], isStat: Boolean,
- superIn: Option[Symbol] = None)(
- implicit pos: Position): js.Tree = {
-
- implicit val pos: Position = tree.pos
-
- def noSpread = !args.exists(_.isInstanceOf[js.JSSpread])
- val argc = args.size // meaningful only for methods that don't have varargs
-
- def requireNotSuper(): Unit = {
- if (superIn.isDefined)
- ctx.error("Illegal super call in Scala.js-defined JS class", tree.pos)
- }
-
- def hasExplicitJSEncoding = {
- sym.hasAnnotation(jsdefn.JSNameAnnot) ||
- sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) ||
- sym.hasAnnotation(jsdefn.JSBracketCallAnnot)
- }
-
- val boxedResult = sym.name match {
- case JSUnaryOpMethodName(code) if argc == 0 =>
- requireNotSuper()
- js.JSUnaryOp(code, receiver)
-
- case JSBinaryOpMethodName(code) if argc == 1 =>
- requireNotSuper()
- js.JSBinaryOp(code, receiver, args.head)
-
- case nme.apply if !hasExplicitJSEncoding =>
- requireNotSuper()
- if (jsdefn.isJSThisFunctionClass(sym.owner))
- js.JSBracketMethodApply(receiver, js.StringLiteral("call"), args)
- else
- js.JSFunctionApply(receiver, args)
-
- case _ =>
- def jsFunName = js.StringLiteral(jsNameOf(sym))
-
- def genSuperReference(propName: js.Tree): js.Tree = {
- superIn.fold[js.Tree] {
- js.JSBracketSelect(receiver, propName)
- } { superInSym =>
- js.JSSuperBracketSelect(
- jstpe.ClassType(encodeClassFullName(superInSym)),
- receiver, propName)
- }
- }
-
- def genSelectGet(propName: js.Tree): js.Tree =
- genSuperReference(propName)
-
- def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree =
- js.Assign(genSuperReference(propName), value)
-
- def genCall(methodName: js.Tree, args: List[js.Tree]): js.Tree = {
- superIn.fold[js.Tree] {
- js.JSBracketMethodApply(
- receiver, methodName, args)
- } { superInSym =>
- js.JSSuperBracketCall(
- jstpe.ClassType(encodeClassFullName(superInSym)),
- receiver, methodName, args)
- }
- }
-
- if (isJSGetter(sym)) {
- assert(noSpread && argc == 0)
- genSelectGet(jsFunName)
- } else if (isJSSetter(sym)) {
- assert(noSpread && argc == 1)
- genSelectSet(jsFunName, args.head)
- } else if (isJSBracketAccess(sym)) {
- assert(noSpread && (argc == 1 || argc == 2),
- s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments")
- args match {
- case List(keyArg) =>
- genSelectGet(keyArg)
- case List(keyArg, valueArg) =>
- genSelectSet(keyArg, valueArg)
- }
- } else if (isJSBracketCall(sym)) {
- val (methodName, actualArgs) = extractFirstArg(args)
- genCall(methodName, actualArgs)
- } else {
- genCall(jsFunName, args)
- }
- }
-
- if (isStat) {
- boxedResult
- } else {
- val tpe = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx =>
- sym.info.finalResultType
- }
- unbox(boxedResult, tpe)
- }
- }
-
- private object JSUnaryOpMethodName {
- private val map = Map(
- nme.UNARY_+ -> js.JSUnaryOp.+,
- nme.UNARY_- -> js.JSUnaryOp.-,
- nme.UNARY_~ -> js.JSUnaryOp.~,
- nme.UNARY_! -> js.JSUnaryOp.!
- )
-
- def unapply(name: Names.TermName): Option[js.JSUnaryOp.Code] =
- map.get(name)
- }
-
- private object JSBinaryOpMethodName {
- private val map = Map(
- nme.ADD -> js.JSBinaryOp.+,
- nme.SUB -> js.JSBinaryOp.-,
- nme.MUL -> js.JSBinaryOp.*,
- nme.DIV -> js.JSBinaryOp./,
- nme.MOD -> js.JSBinaryOp.%,
-
- nme.LSL -> js.JSBinaryOp.<<,
- nme.ASR -> js.JSBinaryOp.>>,
- nme.LSR -> js.JSBinaryOp.>>>,
- nme.OR -> js.JSBinaryOp.|,
- nme.AND -> js.JSBinaryOp.&,
- nme.XOR -> js.JSBinaryOp.^,
-
- nme.LT -> js.JSBinaryOp.<,
- nme.LE -> js.JSBinaryOp.<=,
- nme.GT -> js.JSBinaryOp.>,
- nme.GE -> js.JSBinaryOp.>=,
-
- nme.ZAND -> js.JSBinaryOp.&&,
- nme.ZOR -> js.JSBinaryOp.||
- )
-
- def unapply(name: Names.TermName): Option[js.JSBinaryOp.Code] =
- map.get(name)
- }
-
- /** Extract the first argument in a list of actual arguments.
- *
- * This is nothing else than decomposing into head and tail, except that
- * we assert that the first element is not a JSSpread.
- */
- private def extractFirstArg(args: List[js.Tree]): (js.Tree, List[js.Tree]) = {
- assert(args.nonEmpty,
- "Trying to extract the first argument of an empty argument list")
- val firstArg = args.head
- assert(!firstArg.isInstanceOf[js.JSSpread],
- "Trying to extract the first argument of an argument list starting " +
- "with a Spread argument: " + firstArg)
- (firstArg, args.tail)
- }
-
- /** Gen JS code for a call to a polymorphic method.
- *
- * The only methods that reach the back-end as polymorphic are
- * `isInstanceOf` and `asInstanceOf`.
- *
- * (Well, in fact `DottyRunTime.newRefArray` too, but it is handled as a
- * primitive instead.)
- */
- private def genTypeApply(tree: TypeApply): js.Tree = {
- implicit val pos: Position = tree.pos
-
- val TypeApply(fun, targs) = tree
-
- val sym = fun.symbol
- val receiver = qualifierOf(fun)
-
- val to = targs.head.tpe
-
- assert(!isPrimitiveValueType(receiver.tpe),
- s"Found receiver of type test with primitive type ${receiver.tpe} at $pos")
- assert(!isPrimitiveValueType(to),
- s"Found target type of type test with primitive type ${receiver.tpe} at $pos")
-
- val genReceiver = genExpr(receiver)
-
- if (sym == defn.Any_asInstanceOf) {
- genAsInstanceOf(genReceiver, to)
- } else if (sym == defn.Any_isInstanceOf) {
- genIsInstanceOf(tree, genReceiver, to)
- } else {
- throw new FatalError(
- s"Unexpected type application $fun with symbol ${sym.fullName}")
- }
- }
-
- /** Gen JS code for a Java Seq literal. */
- private def genJavaSeqLiteral(tree: JavaSeqLiteral): js.Tree = {
- implicit val pos: Position = tree.pos
-
- val genElems = tree.elems.map(genExpr)
- val arrayType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType]
- js.ArrayValue(arrayType, genElems)
- }
-
- /** Gen JS code for a closure.
- *
- * Input: a `Closure` tree of the form
- * {{{
- * Closure(env, call, functionalInterface)
- * }}}
- * representing the pseudo-syntax
- * {{{
- * { (p1, ..., pm) => call(env1, ..., envn, p1, ..., pm) }: functionInterface
- * }}}
- * where `envi` are identifiers in the local scope. The qualifier of `call`
- * is also implicitly captured.
- *
- * Output: a `js.Closure` tree of the form
- * {{{
- * js.Closure(formalCaptures, formalParams, body, actualCaptures)
- * }}}
- * representing the pseudo-syntax
- * {{{
- * lambda<formalCapture1 = actualCapture1, ..., formalCaptureN = actualCaptureN>(
- * formalParam1, ..., formalParamM) = body
- * }}}
- * where the `actualCaptures` and `body` are, in general, arbitrary
- * expressions. But in this case, `actualCaptures` will be identifiers from
- * `env`, and the `body` will be of the form
- * {{{
- * call(formalCapture1.ref, ..., formalCaptureN.ref,
- * formalParam1.ref, ...formalParamM.ref)
- * }}}
- *
- * When the `js.Closure` node is evaluated, i.e., when the closure value is
- * created, the expressions of the `actualCaptures` are evaluated, and the
- * results of those evaluations is "stored" in the environment of the
- * closure as the corresponding `formalCapture`.
- *
- * When we later *call* the closure, the `formalCaptures` already have their
- * values from the environment, and they are available in the `body`. The
- * `formalParams` of the created closure receive their values from the
- * actual arguments at the call-site of the closure, and they are also
- * available in the `body`.
- */
- private def genClosure(tree: Closure): js.Tree = {
- implicit val pos: Position = tree.pos
- val Closure(env, call, functionalInterface) = tree
-
- val envSize = env.size
-
- val (fun, args) = call match {
- // case Apply(fun, args) => (fun, args) // Conjectured not to happen
- case t @ Select(_, _) => (t, Nil)
- case t @ Ident(_) => (t, Nil)
- }
- val sym = fun.symbol
-
- val qualifier = qualifierOf(fun)
- val allCaptureValues = qualifier :: env
-
- val (formalCaptures, actualCaptures) = allCaptureValues.map { value =>
- implicit val pos: Position = value.pos
- val formalIdent = value match {
- case Ident(name) => freshLocalIdent(name.toString)
- case This(_) => freshLocalIdent("this")
- case _ => freshLocalIdent()
- }
- val formalCapture =
- js.ParamDef(formalIdent, toIRType(value.tpe), mutable = false, rest = false)
- val actualCapture = genExpr(value)
- (formalCapture, actualCapture)
- }.unzip
-
- val formalParamNames = sym.info.paramNamess.flatten.drop(envSize)
- val formalParamTypes = sym.info.paramTypess.flatten.drop(envSize)
- val (formalParams, actualParams) = formalParamNames.zip(formalParamTypes).map {
- case (name, tpe) =>
- val formalParam = js.ParamDef(freshLocalIdent(name.toString),
- jstpe.AnyType, mutable = false, rest = false)
- val actualParam = unbox(formalParam.ref, tpe)
- (formalParam, actualParam)
- }.unzip
-
- val genBody = {
- val thisCaptureRef :: argCaptureRefs = formalCaptures.map(_.ref)
- val call = genApplyMethod(thisCaptureRef, sym, argCaptureRefs ::: actualParams)
- box(call, sym.info.finalResultType)
- }
-
- val closure = js.Closure(formalCaptures, formalParams, genBody, actualCaptures)
- ctx.debuglog(closure.toString)
-
- val funInterfaceSym = functionalInterface.tpe.widenDealias.typeSymbol
- if (jsdefn.isJSFunctionClass(funInterfaceSym)) {
- closure
- } else {
- assert(!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym),
- s"Invalid functional interface $funInterfaceSym reached the back-end")
- val cls = "sjsr_AnonFunction" + formalParams.size
- val ctor = js.Ident("init___sjs_js_Function" + formalParams.size)
- js.New(jstpe.ClassType(cls), ctor, List(closure))
- }
- }
-
- /** Boxes a value of the given type before `elimErasedValueType`.
- *
- * This should be used when sending values to a JavaScript context, which
- * is erased/boxed at the IR level, although it is not erased at the
- * dotty/JVM level.
- *
- * @param expr Tree to be boxed if needed.
- * @param tpeEnteringElimErasedValueType The type of `expr` as it was
- * entering the `elimErasedValueType` phase.
- */
- private def box(expr: js.Tree, tpeEnteringElimErasedValueType: Type)(
- implicit pos: Position): js.Tree = {
-
- tpeEnteringElimErasedValueType match {
- case tpe if isPrimitiveValueType(tpe) =>
- makePrimitiveBox(expr, tpe)
-
- /*case tpe: ErasedValueType =>
- val boxedClass = tpe.valueClazz
- val ctor = boxedClass.primaryConstructor
- genNew(boxedClass, ctor, List(expr))*/
-
- case _ =>
- expr
- }
- }
-
- /** Unboxes a value typed as Any to the given type before `elimErasedValueType`.
- *
- * This should be used when receiving values from a JavaScript context,
- * which is erased/boxed at the IR level, although it is not erased at the
- * dotty/JVM level.
- *
- * @param expr Tree to be extracted.
- * @param tpeEnteringElimErasedValueType The type of `expr` as it was
- * entering the `elimErasedValueType` phase.
- */
- private def unbox(expr: js.Tree, tpeEnteringElimErasedValueType: Type)(
- implicit pos: Position): js.Tree = {
-
- tpeEnteringElimErasedValueType match {
- case tpe if isPrimitiveValueType(tpe) =>
- makePrimitiveUnbox(expr, tpe)
-
- /*case tpe: ErasedValueType =>
- val boxedClass = tpe.valueClazz
- val unboxMethod = boxedClass.derivedValueClassUnbox
- val content = genApplyMethod(
- genAsInstanceOf(expr, tpe), unboxMethod, Nil)
- if (unboxMethod.tpe.resultType <:< tpe.erasedUnderlying)
- content
- else
- fromAny(content, tpe.erasedUnderlying)*/
-
- case tpe =>
- genAsInstanceOf(expr, tpe)
- }
- }
-
- /** Gen JS code for an asInstanceOf cast (for reference types only) */
- private def genAsInstanceOf(value: js.Tree, to: Type)(
- implicit pos: Position): js.Tree = {
-
- val sym = to.widenDealias.typeSymbol
-
- if (sym == defn.ObjectClass || isJSType(sym)) {
- /* asInstanceOf[Object] always succeeds, and
- * asInstanceOf to a raw JS type is completely erased.
- */
- value
- } else {
- js.AsInstanceOf(value, toReferenceType(to))
- }
- }
-
- /** Gen JS code for an isInstanceOf test (for reference types only) */
- private def genIsInstanceOf(tree: Tree, value: js.Tree, to: Type): js.Tree = {
- implicit val pos: Position = tree.pos
- val sym = to.widenDealias.typeSymbol
-
- if (sym == defn.ObjectClass) {
- js.BinaryOp(js.BinaryOp.!==, value, js.Null())
- } else if (isJSType(sym)) {
- if (sym.is(Trait)) {
- ctx.error(
- s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait",
- tree.pos)
- js.BooleanLiteral(true)
- } else {
- js.Unbox(js.JSBinaryOp(
- js.JSBinaryOp.instanceof, value, genLoadJSConstructor(sym)), 'Z')
- }
- } else {
- js.IsInstanceOf(value, toReferenceType(to))
- }
- }
-
- /** Gen a dynamically linked call to a Scala method. */
- private def genApplyMethod(receiver: js.Tree,
- methodSym: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- js.Apply(receiver, encodeMethodSym(methodSym), arguments)(
- toIRType(patchedResultType(methodSym)))
- }
-
- /** Gen a statically linked call to an instance method. */
- private def genApplyMethodStatically(receiver: js.Tree, method: Symbol,
- arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
- val className = encodeClassFullName(method.owner)
- val methodIdent = encodeMethodSym(method)
- val resultType = toIRType(patchedResultType(method))
- js.ApplyStatically(receiver, jstpe.ClassType(className),
- methodIdent, arguments)(resultType)
- }
-
- /** Gen a call to a static method. */
- private def genApplyStatic(method: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- val cls = jstpe.ClassType(encodeClassFullName(method.owner))
- val methodIdent = encodeMethodSym(method)
- js.ApplyStatic(cls, methodIdent, arguments)(
- toIRType(patchedResultType(method)))
- }
-
- /** Gen a call to a Scala2 impl class method. */
- private def genTraitImplApply(method: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- genApplyStatic(method, arguments)
- }
-
- /** Gen a call to a non-exposed method of a non-native JS class. */
- private def genApplyJSClassMethod(receiver: js.Tree, method: Symbol,
- arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
- genApplyStatic(method, receiver :: arguments)
- }
-
- /** Gen a call to a method of a Scala top-level module. */
- private def genModuleApplyMethod(methodSym: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- genApplyMethod(genLoadModule(methodSym.owner), methodSym, arguments)
- }
-
- /** Gen JS code for `new java.lang.String(...)`.
- *
- * Rewires the instantiation to calling the appropriate overload of
- * `newString` in the object `scala.scalajs.runtime.RuntimeString`.
- */
- private def genNewString(ctor: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- js.Apply(
- genLoadModule(jsdefn.RuntimeStringModuleClass),
- encodeRTStringCtorSym(ctor), arguments)(
- jstpe.ClassType(ir.Definitions.StringClass))
- }
-
- /** Gen a dynamically linked call to a method of java.lang.String.
- *
- * Forwards the call to the module scala.scalajs.runtime.RuntimeString.
- */
- private def genApplyMethodOfString(receiver: js.Tree,
- methodSym: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- js.Apply(
- genLoadModule(jsdefn.RuntimeStringModuleClass),
- encodeRTStringMethodSym(methodSym),
- receiver :: arguments)(
- toIRType(patchedResultType(methodSym)))
- }
-
- /** Gen a boxing operation (tpe is the primitive type) */
- private def makePrimitiveBox(expr: js.Tree, tpe: Type)(
- implicit pos: Position): js.Tree = {
- toReferenceType(tpe) match {
- case jstpe.ClassType(cls) if ir.Definitions.isPrimitiveClass(cls) =>
- assert(cls.length == 1)
- (cls.charAt(0): @switch) match {
- case 'V' =>
- // must be handled at least for JS interop
- js.Block(expr, js.Undefined())
- case 'C' =>
- genModuleApplyMethod(jsdefn.BoxesRunTime_boxToCharacter, List(expr))
- case _ =>
- expr // box is identity for all non-Char types
- }
-
- case _ =>
- throw new FatalError(
- s"makePrimitiveBox requires a primitive type, found $tpe at $pos")
- }
- }
-
- /** Gen an unboxing operation (tpe is the primitive type) */
- private def makePrimitiveUnbox(expr: js.Tree, tpe: Type)(
- implicit pos: Position): js.Tree = {
- toReferenceType(tpe) match {
- case jstpe.ClassType(cls) if ir.Definitions.isPrimitiveClass(cls) =>
- assert(cls.length == 1)
- (cls.charAt(0): @switch) match {
- case 'V' =>
- // must be handled at least for JS interop
- expr
- case 'C' =>
- genModuleApplyMethod(jsdefn.BoxesRunTime_unboxToChar, List(expr))
- case primitiveCharCode =>
- js.Unbox(expr, primitiveCharCode)
- }
-
- case _ =>
- throw new FatalError(
- s"makePrimitiveUnbox requires a primitive type, found $tpe at $pos")
- }
- }
-
- /** Gen actual actual arguments to Scala method call.
- * Returns a list of the transformed arguments.
- *
- * This tries to optimize repeated arguments (varargs) by turning them
- * into js.WrappedArray instead of Scala wrapped arrays.
- */
- private def genActualArgs(sym: Symbol, args: List[Tree])(
- implicit pos: Position): List[js.Tree] = {
- args.map(genExpr)
- /*val wereRepeated = exitingPhase(currentRun.typerPhase) {
- sym.tpe.params.map(p => isScalaRepeatedParamType(p.tpe))
- }
-
- if (wereRepeated.size > args.size) {
- // Should not happen, but let's not crash
- args.map(genExpr)
- } else {
- /* Arguments that are in excess compared to the type signature after
- * erasure are lambda-lifted arguments. They cannot be repeated, hence
- * the extension to `false`.
- */
- for ((arg, wasRepeated) <- args.zipAll(wereRepeated, EmptyTree, false)) yield {
- if (wasRepeated) {
- tryGenRepeatedParamAsJSArray(arg, handleNil = false).fold {
- genExpr(arg)
- } { genArgs =>
- genNew(WrappedArrayClass, WrappedArray_ctor,
- List(js.JSArrayConstr(genArgs)))
- }
- } else {
- genExpr(arg)
- }
- }
- }*/
- }
-
- /** Gen actual actual arguments to a JS method call.
- * Returns a list of the transformed arguments.
- *
- * - TODO Repeated arguments (varargs) are expanded
- * - Default arguments are omitted or replaced by undefined
- * - All arguments are boxed
- *
- * Repeated arguments that cannot be expanded at compile time (i.e., if a
- * Seq is passed to a varargs parameter with the syntax `seq: _*`) will be
- * wrapped in a [[js.JSSpread]] node to be expanded at runtime.
- */
- private def genActualJSArgs(sym: Symbol, args: List[Tree])(
- implicit pos: Position): List[js.Tree] = {
-
- def paramNamesAndTypes(implicit ctx: Context): List[(Names.TermName, Type)] =
- sym.info.paramNamess.flatten.zip(sym.info.paramTypess.flatten)
-
- val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx =>
- for ((name, tpe) <- paramNamesAndTypes)
- yield (name -> tpe.isRepeatedParam)
- }.toMap
-
- val paramTypes = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx =>
- paramNamesAndTypes
- }.toMap
-
- var reversedArgs: List[js.Tree] = Nil
-
- for ((arg, (paramName, paramType)) <- args.zip(paramNamesAndTypes)) {
- val wasRepeated = wereRepeated.getOrElse(paramName, false)
- if (wasRepeated) {
- reversedArgs =
- genJSRepeatedParam(arg) reverse_::: reversedArgs
- } else {
- val unboxedArg = genExpr(arg)
- val boxedArg = unboxedArg match {
- case js.UndefinedParam() =>
- unboxedArg
- case _ =>
- val tpe = paramTypes.getOrElse(paramName, paramType)
- box(unboxedArg, tpe)
- }
- reversedArgs ::= boxedArg
- }
- }
-
- /* Remove all consecutive js.UndefinedParam's at the end of the argument
- * list. No check is performed whether they may be there, since they will
- * only be placed where default arguments can be anyway.
- */
- reversedArgs = reversedArgs.dropWhile(_.isInstanceOf[js.UndefinedParam])
-
- /* Find remaining js.UndefinedParam and replace by js.Undefined. This can
- * happen with named arguments or with multiple argument lists.
- */
- reversedArgs = reversedArgs map {
- case js.UndefinedParam() => js.Undefined()
- case arg => arg
- }
-
- reversedArgs.reverse
- }
-
- /** Gen JS code for a repeated param of a JS method.
- *
- * In this case `arg` has type `Seq[T]` for some `T`, but the result should
- * be an expanded list of the elements in the sequence. So this method
- * takes care of the conversion.
- *
- * It is specialized for the shapes of tree generated by the desugaring
- * of repeated params in Scala, so that these are actually expanded at
- * compile-time.
- *
- * Otherwise, it returns a `JSSpread` with the `Seq` converted to a
- * `js.Array`.
- */
- private def genJSRepeatedParam(arg: Tree): List[js.Tree] = {
- tryGenRepeatedParamAsJSArray(arg, handleNil = true).getOrElse {
- /* Fall back to calling runtime.genTraversableOnce2jsArray
- * to perform the conversion to js.Array, then wrap in a Spread
- * operator.
- */
- implicit val pos: Position = arg.pos
- val jsArrayArg = genModuleApplyMethod(
- jsdefn.RuntimePackage_genTraversableOnce2jsArray,
- List(genExpr(arg)))
- List(js.JSSpread(jsArrayArg))
- }
- }
-
- /** Try and expand an actual argument to a repeated param `(xs: T*)`.
- *
- * This method recognizes the shapes of tree generated by the desugaring
- * of repeated params in Scala, and expands them.
- * If `arg` does not have the shape of a generated repeated param, this
- * method returns `None`.
- */
- private def tryGenRepeatedParamAsJSArray(arg: Tree,
- handleNil: Boolean): Option[List[js.Tree]] = {
- implicit val pos: Position = arg.pos
-
- // Given a method `def foo(args: T*)`
- arg match {
- // foo(arg1, arg2, ..., argN) where N > 0
- case MaybeAsInstanceOf(WrapArray(MaybeAsInstanceOf(array: JavaSeqLiteral))) =>
- /* Value classes in arrays are already boxed, so no need to use
- * the type before erasure.
- * TODO Is this true in dotty?
- */
- Some(array.elems.map(e => box(genExpr(e), e.tpe)))
-
- // foo()
- case Ident(_) if handleNil && arg.symbol == defn.NilModule =>
- Some(Nil)
-
- // foo(argSeq: _*) - cannot be optimized
- case _ =>
- None
- }
- }
-
- private object MaybeAsInstanceOf {
- def unapply(tree: Tree): Some[Tree] = tree match {
- case TypeApply(asInstanceOf_? @ Select(base, _), _)
- if asInstanceOf_?.symbol == defn.Any_asInstanceOf =>
- Some(base)
- case _ =>
- Some(tree)
- }
- }
-
- private object WrapArray {
- lazy val isWrapArray: Set[Symbol] = {
- val names = {
- defn.ScalaValueClasses().map(sym => nme.wrapXArray(sym.name)) ++
- Set(nme.wrapRefArray, nme.genericWrapArray)
- }
- names.map(defn.ScalaPredefModule.requiredMethod(_)).toSet
- }
-
- def unapply(tree: Apply): Option[Tree] = tree match {
- case Apply(wrapArray_?, List(wrapped)) if isWrapArray(wrapArray_?.symbol) =>
- Some(wrapped)
- case _ =>
- None
- }
- }
-
- /** Gen JS code for loading a Java static field.
- */
- private def genLoadStaticField(sym: Symbol)(implicit pos: Position): js.Tree = {
- /* Actually, there is no static member in Scala.js. If we come here, that
- * is because we found the symbol in a Java-emitted .class in the
- * classpath. But the corresponding implementation in Scala.js will
- * actually be a val in the companion module.
- */
-
- if (sym == defn.BoxedUnit_UNIT) {
- js.Undefined()
- } else {
- val instance = genLoadModule(sym.owner)
- val method = encodeStaticMemberSym(sym)
- js.Apply(instance, method, Nil)(toIRType(sym.info))
- }
- }
-
- /** Gen JS code for loading a module.
- *
- * Can be given either the module symbol, or its module class symbol.
- */
- private def genLoadModule(sym0: Symbol)(implicit pos: Position): js.Tree = {
- require(sym0.is(Module),
- "genLoadModule called with non-module symbol: " + sym0)
- val sym1 = if (sym0.isTerm) sym0.moduleClass else sym0
- val sym = // redirect all static methods of String to RuntimeString
- if (sym1 == defn.StringModule) jsdefn.RuntimeStringModule.moduleClass
- else sym1
-
- if (isJSType(sym)) {
- if (isScalaJSDefinedJSClass(sym))
- js.LoadJSModule(jstpe.ClassType(encodeClassFullName(sym)))
- else if (sym.derivesFrom(jsdefn.JSGlobalScopeClass))
- genLoadJSGlobal()
- else
- genLoadNativeJSModule(sym)
- } else {
- js.LoadModule(jstpe.ClassType(encodeClassFullName(sym)))
- }
- }
-
- /** Gen JS code representing the constructor of a JS class. */
- private def genLoadJSConstructor(sym: Symbol)(
- implicit pos: Position): js.Tree = {
- assert(!isStaticModule(sym) && !sym.is(Trait),
- s"genPrimitiveJSClass called with non-class $sym")
- js.LoadJSConstructor(jstpe.ClassType(encodeClassFullName(sym)))
- }
-
- /** Gen JS code representing a native JS module. */
- private def genLoadNativeJSModule(sym: Symbol)(
- implicit pos: Position): js.Tree = {
- require(sym.is(ModuleClass),
- s"genLoadNativeJSModule called with non-module $sym")
- fullJSNameOf(sym).split('.').foldLeft(genLoadJSGlobal()) { (memo, chunk) =>
- js.JSBracketSelect(memo, js.StringLiteral(chunk))
- }
- }
-
- /** Gen JS code to load the JavaScript global scope. */
- private def genLoadJSGlobal()(implicit pos: Position): js.Tree = {
- js.JSBracketSelect(
- js.JSBracketSelect(js.JSLinkingInfo(), js.StringLiteral("envInfo")),
- js.StringLiteral("global"))
- }
-
- /** Generate a Class[_] value (e.g. coming from classOf[T]) */
- private def genClassConstant(tpe: Type)(implicit pos: Position): js.Tree =
- js.ClassOf(toReferenceType(tpe))
-
- private def isStaticModule(sym: Symbol): Boolean =
- sym.is(Module) && sym.isStatic
-
- private def isPrimitiveValueType(tpe: Type): Boolean = {
- tpe.widenDealias match {
- case JavaArrayType(_) => false
- case t => t.typeSymbol.asClass.isPrimitiveValueClass
- }
- }
-
-}
diff --git a/src/dotty/tools/backend/sjs/JSDefinitions.scala b/src/dotty/tools/backend/sjs/JSDefinitions.scala
deleted file mode 100644
index bd0b74031..000000000
--- a/src/dotty/tools/backend/sjs/JSDefinitions.scala
+++ /dev/null
@@ -1,199 +0,0 @@
-package dotty.tools.backend.sjs
-
-import dotty.tools.dotc.core._
-
-import Types._
-import Contexts._
-import Symbols._
-import Names._
-import StdNames._
-import Decorators._
-
-import dotty.tools.dotc.config.SJSPlatform
-
-object JSDefinitions {
- /** The Scala.js-specific definitions for the current context. */
- def jsdefn(implicit ctx: Context): JSDefinitions =
- ctx.platform.asInstanceOf[SJSPlatform].jsDefinitions
-}
-
-final class JSDefinitions()(implicit ctx: Context) {
-
- lazy val InlineAnnotType: TypeRef = ctx.requiredClassRef("scala.inline")
- def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass
- lazy val NoinlineAnnotType: TypeRef = ctx.requiredClassRef("scala.noinline")
- def NoinlineAnnot(implicit ctx: Context) = NoinlineAnnotType.symbol.asClass
-
- lazy val ScalaJSJSPackageVal = ctx.requiredPackage("scala.scalajs.js")
- lazy val ScalaJSJSPackageClass = ScalaJSJSPackageVal.moduleClass.asClass
- lazy val JSPackage_typeOfR = ScalaJSJSPackageClass.requiredMethodRef("typeOf")
- def JSPackage_typeOf(implicit ctx: Context) = JSPackage_typeOfR.symbol
- lazy val JSPackage_constructorOfR = ScalaJSJSPackageClass.requiredMethodRef("constructorOf")
- def JSPackage_constructorOf(implicit ctx: Context) = JSPackage_constructorOfR.symbol
- lazy val JSPackage_debuggerR = ScalaJSJSPackageClass.requiredMethodRef("debugger")
- def JSPackage_debugger(implicit ctx: Context) = JSPackage_debuggerR.symbol
- lazy val JSPackage_nativeR = ScalaJSJSPackageClass.requiredMethodRef("native")
- def JSPackage_native(implicit ctx: Context) = JSPackage_nativeR.symbol
-
- lazy val JSNativeAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.native")
- def JSNativeAnnot(implicit ctx: Context) = JSNativeAnnotType.symbol.asClass
-
- lazy val JSAnyType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Any")
- def JSAnyClass(implicit ctx: Context) = JSAnyType.symbol.asClass
- lazy val JSObjectType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Object")
- def JSObjectClass(implicit ctx: Context) = JSObjectType.symbol.asClass
- lazy val JSBaseThisFunctionType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.ThisFunction")
- def JSBaseThisFunctionClass(implicit ctx: Context) = JSBaseThisFunctionType.symbol.asClass
-
- lazy val JSDictionaryType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Dictionary")
- def JSDictionaryClass(implicit ctx: Context) = JSDictionaryType.symbol.asClass
- lazy val JSDictionary_deleteR = JSDictionaryClass.requiredMethodRef("delete")
- def JSDictionary_delete(implicit ctx: Context) = JSDictionary_deleteR.symbol
-
- lazy val JSGlobalScopeType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.GlobalScope")
- def JSGlobalScopeClass(implicit ctx: Context) = JSGlobalScopeType.symbol.asClass
-
- lazy val JSArrayType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Array")
- def JSArrayClass(implicit ctx: Context) = JSArrayType.symbol.asClass
-
- lazy val JSFunctionType = (0 to 22).map(n => ctx.requiredClassRef("scala.scalajs.js.Function" + n)).toArray
- def JSFunctionClass(n: Int)(implicit ctx: Context) = JSFunctionType(n).symbol.asClass
- lazy val JSThisFunctionType = (0 to 21).map(n => ctx.requiredClassRef("scala.scalajs.js.ThisFunction" + n)).toArray
- def JSThisFunctionClass(n: Int)(implicit ctx: Context) = JSThisFunctionType(n).symbol.asClass
-
- lazy val RuntimeExceptionType: TypeRef = ctx.requiredClassRef("java.lang.RuntimeException")
- def RuntimeExceptionClass(implicit ctx: Context) = RuntimeExceptionType.symbol.asClass
- lazy val JavaScriptExceptionType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.JavaScriptException")
- def JavaScriptExceptionClass(implicit ctx: Context) = JavaScriptExceptionType.symbol.asClass
-
- lazy val JSNameAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSName")
- def JSNameAnnot(implicit ctx: Context) = JSNameAnnotType.symbol.asClass
- lazy val JSFullNameAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSFullName")
- def JSFullNameAnnot(implicit ctx: Context) = JSFullNameAnnotType.symbol.asClass
- lazy val JSBracketAccessAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSBracketAccess")
- def JSBracketAccessAnnot(implicit ctx: Context) = JSBracketAccessAnnotType.symbol.asClass
- lazy val JSBracketCallAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSBracketCall")
- def JSBracketCallAnnot(implicit ctx: Context) = JSBracketCallAnnotType.symbol.asClass
- lazy val JSExportAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExport")
- def JSExportAnnot(implicit ctx: Context) = JSExportAnnotType.symbol.asClass
- lazy val JSExportDescendentObjectsAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentObjects")
- def JSExportDescendentObjectsAnnot(implicit ctx: Context) = JSExportDescendentObjectsAnnotType.symbol.asClass
- lazy val JSExportDescendentClassesAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentClasses")
- def JSExportDescendentClassesAnnot(implicit ctx: Context) = JSExportDescendentClassesAnnotType.symbol.asClass
- lazy val JSExportAllAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportAll")
- def JSExportAllAnnot(implicit ctx: Context) = JSExportAllAnnotType.symbol.asClass
- lazy val JSExportNamedAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportNamed")
- def JSExportNamedAnnot(implicit ctx: Context) = JSExportNamedAnnotType.symbol.asClass
- lazy val RawJSTypeAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.RawJSType")
- def RawJSTypeAnnot(implicit ctx: Context) = RawJSTypeAnnotType.symbol.asClass
- lazy val ExposedJSMemberAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.ExposedJSMember")
- def ExposedJSMemberAnnot(implicit ctx: Context) = ExposedJSMemberAnnotType.symbol.asClass
-
- lazy val JSAnyModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Any")
- def JSAnyModule(implicit ctx: Context) = JSAnyModuleRef.symbol
- lazy val JSAny_fromFunctionR = (0 to 22).map(n => JSAnyModule.requiredMethodRef("fromFunction" + n)).toArray
- def JSAny_fromFunction(n: Int)(implicit ctx: Context) = JSAny_fromFunctionR(n).symbol
-
- lazy val JSDynamicModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Dynamic")
- def JSDynamicModule(implicit ctx: Context) = JSDynamicModuleRef.symbol
- lazy val JSDynamic_newInstanceR = JSDynamicModule.requiredMethodRef("newInstance")
- def JSDynamic_newInstance(implicit ctx: Context) = JSDynamic_newInstanceR.symbol
-
- lazy val JSDynamicLiteralModuleRef = JSDynamicModule.moduleClass.requiredValueRef("literal")
- def JSDynamicLiteralModule(implicit ctx: Context) = JSDynamicLiteralModuleRef.symbol
- lazy val JSDynamicLiteral_applyDynamicNamedR = JSDynamicLiteralModule.requiredMethodRef("applyDynamicNamed")
- def JSDynamicLiteral_applyDynamicNamed(implicit ctx: Context) = JSDynamicLiteral_applyDynamicNamedR.symbol
- lazy val JSDynamicLiteral_applyDynamicR = JSDynamicLiteralModule.requiredMethodRef("applyDynamic")
- def JSDynamicLiteral_applyDynamic(implicit ctx: Context) = JSDynamicLiteral_applyDynamicR.symbol
-
- lazy val JSObjectModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Object")
- def JSObjectModule(implicit ctx: Context) = JSObjectModuleRef.symbol
- lazy val JSObject_hasPropertyR = JSObjectModule.requiredMethodRef("hasProperty")
- def JSObject_hasProperty(implicit ctx: Context) = JSObject_hasPropertyR.symbol
- lazy val JSObject_propertiesR = JSObjectModule.requiredMethodRef("properties")
- def JSObject_properties(implicit ctx: Context) = JSObject_propertiesR.symbol
-
- lazy val JSArrayModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Array")
- def JSArrayModule(implicit ctx: Context) = JSArrayModuleRef.symbol
- lazy val JSArray_applyR = JSArrayModule.requiredMethodRef(nme.apply)
- def JSArray_apply(implicit ctx: Context) = JSArray_applyR.symbol
-
- lazy val JSThisFunctionModuleRef = ctx.requiredModuleRef("scala.scalajs.js.ThisFunction")
- def JSThisFunctionModule(implicit ctx: Context) = JSThisFunctionModuleRef.symbol
- lazy val JSThisFunction_fromFunctionR = (1 to 22).map(n => JSThisFunctionModule.requiredMethodRef("fromFunction" + n)).toArray
- def JSThisFunction_fromFunction(n: Int)(implicit ctx: Context) = JSThisFunction_fromFunctionR(n - 1).symbol
-
- lazy val JSConstructorTagModuleRef = ctx.requiredModuleRef("scala.scalajs.js.ConstructorTag")
- def JSConstructorTagModule(implicit ctx: Context) = JSConstructorTagModuleRef.symbol
- lazy val JSConstructorTag_materializeR = JSConstructorTagModule.requiredMethodRef("materialize")
- def JSConstructorTag_materialize(implicit ctx: Context) = JSConstructorTag_materializeR.symbol
-
- lazy val RuntimeStringModuleRef = ctx.requiredModuleRef("scala.scalajs.runtime.RuntimeString")
- def RuntimeStringModule(implicit ctx: Context) = RuntimeStringModuleRef.symbol
- def RuntimeStringModuleClass(implicit ctx: Context) = RuntimeStringModule.moduleClass.asClass
-
- lazy val BooleanReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.BooleanReflectiveCall")
- def BooleanReflectiveCallClass(implicit ctx: Context) = BooleanReflectiveCallType.symbol.asClass
- lazy val NumberReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.NumberReflectiveCall")
- def NumberReflectiveCallClass(implicit ctx: Context) = NumberReflectiveCallType.symbol.asClass
- lazy val IntegerReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.IntegerReflectiveCall")
- def IntegerReflectiveCallClass(implicit ctx: Context) = IntegerReflectiveCallType.symbol.asClass
- lazy val LongReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.LongReflectiveCall")
- def LongReflectiveCallClass(implicit ctx: Context) = LongReflectiveCallType.symbol.asClass
-
- lazy val RuntimePackageVal = ctx.requiredPackage("scala.scalajs.runtime")
- lazy val RuntimePackageClass = RuntimePackageVal.moduleClass.asClass
- lazy val RuntimePackage_wrapJavaScriptExceptionR = RuntimePackageClass.requiredMethodRef("wrapJavaScriptException")
- def RuntimePackage_typeOf(implicit ctx: Context) = RuntimePackage_wrapJavaScriptExceptionR.symbol
- lazy val RuntimePackage_unwrapJavaScriptExceptionR = RuntimePackageClass.requiredMethodRef("unwrapJavaScriptException")
- def RuntimePackage_unwrapJavaScriptException(implicit ctx: Context) = RuntimePackage_unwrapJavaScriptExceptionR.symbol
- lazy val RuntimePackage_genTraversableOnce2jsArrayR = RuntimePackageClass.requiredMethodRef("genTraversableOnce2jsArray")
- def RuntimePackage_genTraversableOnce2jsArray(implicit ctx: Context) = RuntimePackage_genTraversableOnce2jsArrayR.symbol
- lazy val RuntimePackage_jsTupleArray2jsObjectR = RuntimePackageClass.requiredMethodRef("jsTupleArray2jsObject")
- def RuntimePackage_jsTupleArray2jsObject(implicit ctx: Context) = RuntimePackage_jsTupleArray2jsObjectR.symbol
- lazy val RuntimePackage_constructorOfR = RuntimePackageClass.requiredMethodRef("constructorOf")
- def RuntimePackage_constructorOf(implicit ctx: Context) = RuntimePackage_constructorOfR.symbol
- lazy val RuntimePackage_newConstructorTagR = RuntimePackageClass.requiredMethodRef("newConstructorTag")
- def RuntimePackage_newConstructorTag(implicit ctx: Context) = RuntimePackage_newConstructorTagR.symbol
- lazy val RuntimePackage_propertiesOfR = RuntimePackageClass.requiredMethodRef("propertiesOf")
- def RuntimePackage_propertiesOf(implicit ctx: Context) = RuntimePackage_propertiesOfR.symbol
- lazy val RuntimePackage_environmentInfoR = RuntimePackageClass.requiredMethodRef("environmentInfo")
- def RuntimePackage_environmentInfo(implicit ctx: Context) = RuntimePackage_environmentInfoR.symbol
- lazy val RuntimePackage_linkingInfoR = RuntimePackageClass.requiredMethodRef("linkingInfo")
- def RuntimePackage_linkingInfo(implicit ctx: Context) = RuntimePackage_linkingInfoR.symbol
-
- lazy val WrappedArrayType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.WrappedArray")
- def WrappedArrayClass(implicit ctx: Context) = WrappedArrayType.symbol.asClass
-
- lazy val ScalaRunTime_isArrayR = defn.ScalaRuntimeModule.requiredMethodRef("isArray", List(???, ???))
- def ScalaRunTime_isArray(implicit ctx: Context): Symbol = ScalaRunTime_isArrayR.symbol
-
- lazy val BoxesRunTime_boxToCharacterR = defn.BoxesRunTimeModule.requiredMethodRef("boxToCharacter")
- def BoxesRunTime_boxToCharacter(implicit ctx: Context): Symbol = BoxesRunTime_boxToCharacterR.symbol
- lazy val BoxesRunTime_unboxToCharR = defn.BoxesRunTimeModule.requiredMethodRef("unboxToChar")
- def BoxesRunTime_unboxToChar(implicit ctx: Context): Symbol = BoxesRunTime_unboxToCharR.symbol
-
- /** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
- private def scalajsClassName(cls: Symbol)(implicit ctx: Context): TypeName =
- if (cls.isClass && cls.owner == ScalaJSJSPackageClass) cls.asClass.name
- else EmptyTypeName
-
- /** Is the given `cls` a class of the form `scala.scalajs.js.prefixN` where
- * `N` is a number.
- *
- * This is similar to `isVarArityClass` in `Definitions.scala`.
- */
- private def isScalaJSVarArityClass(cls: Symbol, prefix: Name): Boolean = {
- val name = scalajsClassName(cls)
- name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
- }
-
- def isJSFunctionClass(cls: Symbol): Boolean =
- isScalaJSVarArityClass(cls, nme.Function)
-
- private val ThisFunctionName = termName("ThisFunction")
-
- def isJSThisFunctionClass(cls: Symbol): Boolean =
- isScalaJSVarArityClass(cls, ThisFunctionName)
-
-}
diff --git a/src/dotty/tools/backend/sjs/JSEncoding.scala b/src/dotty/tools/backend/sjs/JSEncoding.scala
deleted file mode 100644
index e8ea3258b..000000000
--- a/src/dotty/tools/backend/sjs/JSEncoding.scala
+++ /dev/null
@@ -1,389 +0,0 @@
-package dotty.tools.backend.sjs
-
-import scala.collection.mutable
-
-import dotty.tools.FatalError
-
-import dotty.tools.dotc.core._
-import Periods._
-import SymDenotations._
-import Contexts._
-import Types._
-import Symbols._
-import Denotations._
-import NameOps._
-import StdNames._
-
-import org.scalajs.core.ir
-import ir.{Trees => js, Types => jstpe}
-
-import ScopedVar.withScopedVars
-import JSDefinitions._
-import JSInterop._
-
-/** Encoding of symbol names for JavaScript
- *
- * Some issues that this encoding solves:
- * * Overloading: encode the full signature in the JS name
- * * Same scope for fields and methods of a class
- * * Global access to classes and modules (by their full name)
- *
- * @author Sébastien Doeraene
- */
-object JSEncoding {
-
- /** Signature separator string (between parameter types) */
- private final val SignatureSep = "__"
-
- /** Name given to the local Scala.js environment variable */
- private final val ScalaJSEnvironmentName = "ScalaJS"
-
- implicit class SymOps(val self: Symbol) extends AnyVal {
- def unexpandedName(implicit ctx: Context): Names.Name =
- self.name.unexpandedName
- }
-
- implicit class MyNameOps(val self: Names.Name) extends AnyVal {
- def decoded: String = self.decode.toString
- }
-
- // Fresh local name generator ----------------------------------------------
-
- class LocalNameGenerator {
- import LocalNameGenerator._
-
- private val usedLocalNames = mutable.Set.empty[String]
- private val localSymbolNames = mutable.Map.empty[Symbol, String]
-
- def localSymbolName(sym: Symbol)(implicit ctx: Context): String =
- localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString))
-
- def freshLocalIdent()(implicit pos: ir.Position): js.Ident =
- js.Ident(freshName(), None)
-
- def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident =
- js.Ident(freshName(base), Some(base))
-
- private def freshName(base: String = "x"): String = {
- var suffix = 1
- var longName = base
- while (usedLocalNames(longName) || isReserved(longName)) {
- suffix += 1
- longName = base+"$"+suffix
- }
- usedLocalNames += longName
- mangleJSName(longName)
- }
- }
-
- private object LocalNameGenerator {
- private val isReserved =
- Set("arguments", "eval", ScalaJSEnvironmentName)
- }
-
- // Encoding methods ----------------------------------------------------------
-
- def encodeLabelSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = {
- require(sym.is(Flags.Label), "encodeLabelSym called with non-label symbol: " + sym)
- js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded))
- }
-
- private def allRefClasses(implicit ctx: Context): Set[Symbol] = {
- //TODO
- /*(Set(ObjectRefClass, VolatileObjectRefClass) ++
- refClass.values ++ volatileRefClass.values)*/
- Set()
- }
-
- def encodeFieldSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position): js.Ident = {
- require(sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module),
- "encodeFieldSym called with non-field symbol: " + sym)
-
- val name0 = encodeMemberNameInternal(sym)
- val name =
- if (name0.charAt(name0.length()-1) != ' ') name0
- else name0.substring(0, name0.length()-1)
-
- /* We have to special-case fields of Ref types (IntRef, ObjectRef, etc.)
- * because they are emitted as private by our .scala source files, but
- * they are considered public at use site since their symbols come from
- * Java-emitted .class files.
- */
- val idSuffix =
- if (sym.is(Flags.Private) || allRefClasses.contains(sym.owner))
- sym.owner.asClass.baseClasses.size.toString
- else
- "f"
-
- val encodedName = name + "$" + idSuffix
- js.Ident(mangleJSName(encodedName), Some(sym.unexpandedName.decoded))
- }
-
- def encodeMethodSym(sym: Symbol, reflProxy: Boolean = false)(
- implicit ctx: Context, pos: ir.Position): js.Ident = {
- val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy)
- js.Ident(encodedName + paramsString,
- Some(sym.unexpandedName.decoded + paramsString))
- }
-
- def encodeMethodName(sym: Symbol, reflProxy: Boolean = false)(
- implicit ctx: Context): String = {
- val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy)
- encodedName + paramsString
- }
-
- /** Encodes a method symbol of java.lang.String for use in RuntimeString.
- *
- * This basically means adding an initial parameter of type
- * java.lang.String, which is the `this` parameter.
- */
- def encodeRTStringMethodSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position): js.Ident = {
- require(sym.owner == defn.StringClass)
- require(!sym.isClassConstructor && !sym.is(Flags.Private))
-
- val (encodedName, paramsString) =
- encodeMethodNameInternal(sym, inRTClass = true)
- js.Ident(encodedName + paramsString,
- Some(sym.unexpandedName.decoded + paramsString))
- }
-
- /** Encodes a constructor symbol of java.lang.String for use in RuntimeString.
- *
- * - The name is rerouted to `newString`
- * - The result type is set to `java.lang.String`
- */
- def encodeRTStringCtorSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position): js.Ident = {
- require(sym.owner == defn.StringClass)
- require(sym.isClassConstructor && !sym.is(Flags.Private))
-
- val paramTypeNames = sym.info.firstParamTypes.map(internalName(_))
- val paramAndResultTypeNames = paramTypeNames :+ ir.Definitions.StringClass
- val paramsString = makeParamsString(paramAndResultTypeNames)
-
- js.Ident("newString" + paramsString,
- Some(sym.unexpandedName.decoded + paramsString))
- }
-
- private def encodeMethodNameInternal(sym: Symbol,
- reflProxy: Boolean = false, inRTClass: Boolean = false)(
- implicit ctx: Context): (String, String) = {
- require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym)
-
- def name = encodeMemberNameInternal(sym)
-
- val encodedName = {
- if (sym.isClassConstructor) {
- "init_"
- } else if (sym.is(Flags.Private)) {
- (mangleJSName(name) + SignatureSep + "p" +
- sym.owner.asClass.baseClasses.size.toString)
- } else {
- mangleJSName(name)
- }
- }
-
- val paramsString = makeParamsString(sym, reflProxy, inRTClass)
-
- (encodedName, paramsString)
- }
-
- def encodeStaticMemberSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position): js.Ident = {
- require(sym.is(Flags.JavaStaticTerm),
- "encodeStaticMemberSym called with non-static symbol: " + sym)
- js.Ident(
- mangleJSName(encodeMemberNameInternal(sym)) +
- makeParamsString(List(internalName(sym.info))),
- Some(sym.unexpandedName.decoded))
- }
-
- def encodeLocalSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = {
- require(!sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module),
- "encodeLocalSym called with non-local symbol: " + sym)
- js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded))
- }
-
- def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean =
- sym.name.isImplClassName
-
- def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = {
- if (sym == defn.ObjectClass) jstpe.AnyType
- else if (isJSType(sym)) jstpe.AnyType
- else {
- assert(sym != defn.ArrayClass,
- "encodeClassType() cannot be called with ArrayClass")
- jstpe.ClassType(encodeClassFullName(sym))
- }
- }
-
- def encodeClassFullNameIdent(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position): js.Ident = {
- js.Ident(encodeClassFullName(sym), Some(sym.fullName.toString))
- }
-
- def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String = {
- if (sym == defn.NothingClass) ir.Definitions.RuntimeNothingClass
- else if (sym == defn.NullClass) ir.Definitions.RuntimeNullClass
- else ir.Definitions.encodeClassName(sym.fullName.toString)
- }
-
- private def encodeMemberNameInternal(sym: Symbol)(
- implicit ctx: Context): String = {
- sym.name.toString.replace("_", "$und").replace("~", "$tilde")
- }
-
- def toIRType(tp: Type)(implicit ctx: Context): jstpe.Type = {
- val refType = toReferenceTypeInternal(tp)
- refType._1 match {
- case tpe: jstpe.ClassType =>
- val sym = refType._2
- if (sym.asClass.isPrimitiveValueClass) {
- if (sym == defn.BooleanClass)
- jstpe.BooleanType
- else if (sym == defn.FloatClass)
- jstpe.FloatType
- else if (sym == defn.DoubleClass)
- jstpe.DoubleType
- else if (sym == defn.LongClass)
- jstpe.LongType
- else if (sym == defn.UnitClass)
- jstpe.NoType
- else
- jstpe.IntType
- } else {
- if (sym == defn.ObjectClass || isJSType(sym))
- jstpe.AnyType
- else if (sym == defn.NothingClass)
- jstpe.NothingType
- else if (sym == defn.NullClass)
- jstpe.NullType
- else
- tpe
- }
-
- case tpe: jstpe.ArrayType =>
- tpe
- }
- }
-
- def toReferenceType(tp: Type)(implicit ctx: Context): jstpe.ReferenceType =
- toReferenceTypeInternal(tp)._1
-
- private def toReferenceTypeInternal(tp: Type)(
- implicit ctx: Context): (jstpe.ReferenceType, Symbol) = {
-
- /**
- * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int.
- * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType.
- */
- def primitiveOrClassToRefType(sym: Symbol): (jstpe.ReferenceType, Symbol) = {
- assert(sym.isClass, sym)
- //assert(sym != defn.ArrayClass || isCompilingArray, sym)
- (jstpe.ClassType(encodeClassFullName(sym)), sym)
- }
-
- /**
- * When compiling Array.scala, the type parameter T is not erased and shows up in method
- * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference.
- */
- def nonClassTypeRefToRefType(sym: Symbol): (jstpe.ReferenceType, Symbol) = {
- //assert(sym.isType && isCompilingArray, sym)
- (jstpe.ClassType(ir.Definitions.ObjectClass), defn.ObjectClass)
- }
-
- tp.widenDealias match {
- // Array type such as Array[Int] (kept by erasure)
- case JavaArrayType(el) =>
- val elRefType = toReferenceTypeInternal(el)
- (jstpe.ArrayType(elRefType._1), elRefType._2)
-
- case t: TypeRef =>
- if (!t.symbol.isClass) nonClassTypeRefToRefType(t.symbol) // See comment on nonClassTypeRefToBType
- else primitiveOrClassToRefType(t.symbol) // Common reference to a type such as scala.Int or java.lang.String
-
- case Types.ClassInfo(_, sym, _, _, _) =>
- /* We get here, for example, for genLoadModule, which invokes
- * toTypeKind(moduleClassSymbol.info)
- */
- primitiveOrClassToRefType(sym)
-
- case t: MethodType => // triggers for LabelDefs
- toReferenceTypeInternal(t.resultType)
-
- /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for
- * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning.
- * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala.
- */
- case a @ AnnotatedType(t, _) =>
- //debuglog(s"typeKind of annotated type $a")
- toReferenceTypeInternal(t)
- }
- }
-
- /** Patches the result type of a method symbol to sanitize it.
- *
- * For some reason, dotc thinks that the `info.resultType`of an
- * `isConstructor` method (for classes or traits) is the enclosing class
- * or trait, but the bodies and usages act as if the result type was `Unit`.
- *
- * This method returns `UnitType` for constructor methods, and otherwise
- * `sym.info.resultType`.
- */
- def patchedResultType(sym: Symbol)(implicit ctx: Context): Type =
- if (sym.isConstructor) defn.UnitType
- else sym.info.resultType
-
- // Encoding of method signatures
-
- private def makeParamsString(sym: Symbol, reflProxy: Boolean,
- inRTClass: Boolean)(
- implicit ctx: Context): String = {
- val tpe = sym.info
-
- val paramTypeNames0 = tpe.firstParamTypes.map(internalName(_))
-
- val hasExplicitThisParameter =
- inRTClass || isScalaJSDefinedJSClass(sym.owner)
- val paramTypeNames =
- if (!hasExplicitThisParameter) paramTypeNames0
- else encodeClassFullName(sym.owner) :: paramTypeNames0
-
- val paramAndResultTypeNames = {
- if (sym.isClassConstructor)
- paramTypeNames
- else if (reflProxy)
- paramTypeNames :+ ""
- else
- paramTypeNames :+ internalName(patchedResultType(sym))
- }
- makeParamsString(paramAndResultTypeNames)
- }
-
- private def makeParamsString(paramAndResultTypeNames: List[String]) =
- paramAndResultTypeNames.mkString(SignatureSep, SignatureSep, "")
-
- /** Computes the internal name for a type. */
- private def internalName(tpe: Type)(implicit ctx: Context): String =
- encodeReferenceType(toReferenceType(tpe))
-
- /** Encodes a [[Types.ReferenceType]], such as in an encoded method signature.
- */
- private def encodeReferenceType(refType: jstpe.ReferenceType): String = {
- refType match {
- case jstpe.ClassType(encodedName) => encodedName
- case jstpe.ArrayType(base, depth) => "A" * depth + base
- }
- }
-
- /** Mangles names that are illegal in JavaScript by prepending a `$`.
- * Also mangles names that would collide with these mangled names.
- */
- private def mangleJSName(name: String) =
- if (js.isKeyword(name) || name(0).isDigit || name(0) == '$') "$" + name
- else name
-}
diff --git a/src/dotty/tools/backend/sjs/JSInterop.scala b/src/dotty/tools/backend/sjs/JSInterop.scala
deleted file mode 100644
index 6d66c3206..000000000
--- a/src/dotty/tools/backend/sjs/JSInterop.scala
+++ /dev/null
@@ -1,110 +0,0 @@
-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
- }
- }
-
-}
diff --git a/src/dotty/tools/backend/sjs/JSPositions.scala b/src/dotty/tools/backend/sjs/JSPositions.scala
deleted file mode 100644
index 10570da00..000000000
--- a/src/dotty/tools/backend/sjs/JSPositions.scala
+++ /dev/null
@@ -1,65 +0,0 @@
-package dotty.tools.backend.sjs
-
-import dotty.tools.dotc.core._
-import Contexts._
-import dotty.tools.dotc.util.Positions
-import Positions.Position
-
-import org.scalajs.core.ir
-
-/** Conversion utilities from dotty Positions to IR Positions. */
-class JSPositions()(implicit ctx: Context) {
-
- /** Implicit conversion from dotty Position to ir.Position. */
- implicit def pos2irPos(pos: Positions.Position): ir.Position = {
- if (!pos.exists) ir.Position.NoPosition
- else {
- val source = pos2irPosCache.toIRSource(ctx.compilationUnit.source)
- val sourcePos = ctx.compilationUnit.source.atPos(pos)
- // dotty positions are 1-based but IR positions are 0-based
- ir.Position(source, sourcePos.line-1, sourcePos.column-1)
- }
- }
-
- /** Implicitly materializes an ir.Position from an implicit dotty Position. */
- implicit def implicitPos2irPos(
- implicit pos: Positions.Position): ir.Position = {
- pos2irPos(pos)
- }
-
- private[this] object pos2irPosCache { // scalastyle:ignore
- import dotty.tools.dotc.util._
-
- private[this] var lastDotcSource: SourceFile = null
- private[this] var lastIRSource: ir.Position.SourceFile = null
-
- def toIRSource(dotcSource: SourceFile): ir.Position.SourceFile = {
- if (dotcSource != lastDotcSource) {
- lastIRSource = convert(dotcSource)
- lastDotcSource = dotcSource
- }
- lastIRSource
- }
-
- private[this] def convert(dotcSource: SourceFile): ir.Position.SourceFile = {
- dotcSource.file.file match {
- case null =>
- new java.net.URI(
- "virtualfile", // Pseudo-Scheme
- dotcSource.file.path, // Scheme specific part
- null // Fragment
- )
- case file =>
- val srcURI = file.toURI
- def matches(pat: java.net.URI) = pat.relativize(srcURI) != srcURI
-
- // TODO
- /*scalaJSOpts.sourceURIMaps.collectFirst {
- case ScalaJSOptions.URIMap(from, to) if matches(from) =>
- val relURI = from.relativize(srcURI)
- to.fold(relURI)(_.resolve(relURI))
- } getOrElse*/ srcURI
- }
- }
- }
-}
diff --git a/src/dotty/tools/backend/sjs/JSPrimitives.scala b/src/dotty/tools/backend/sjs/JSPrimitives.scala
deleted file mode 100644
index 6c3c5715c..000000000
--- a/src/dotty/tools/backend/sjs/JSPrimitives.scala
+++ /dev/null
@@ -1,118 +0,0 @@
-package dotty.tools.backend.sjs
-
-import dotty.tools.dotc.core._
-import Names.TermName
-import StdNames._
-import Types._
-import Contexts._
-import Symbols._
-
-import dotty.tools.dotc.ast.tpd._
-import dotty.tools.dotc.backend.jvm.DottyPrimitives
-
-import scala.collection.mutable
-
-object JSPrimitives {
-
- final val GETCLASS = 301 // jl.Object.getClass()
-
- final val F2JS = 302 // js.Any.fromFunctionN
- final val F2JSTHIS = 303 // js.ThisFunction.fromFunctionN
-
- final val DYNNEW = 304 // js.Dynamic.newInstance
- final val DYNLIT = 305 // js.Dynamic.literal.applyDynamic{,Named}
- final val DICT_DEL = 306 // js.Dictionary.delete
- final val ARR_CREATE = 307 // js.Array.apply (array literal syntax)
-
- final val TYPEOF = 308 // js.typeOf(x)
- final val DEBUGGER = 309 // js.debugger()
- final val HASPROP = 310 // js.Object.hasProperty(o, p), equiv to `p in o` in JS
- final val OBJPROPS = 311 // js.Object.properties(o), equiv to `for (p in o)` in JS
- final val JS_NATIVE = 312 // js.native. Marker method. Fails if tried to be emitted.
-
- final val UNITVAL = 313 // () value, which is undefined
- final val UNITTYPE = 314 // BoxedUnit.TYPE (== classOf[Unit])
-
- final val CONSTRUCTOROF = 315 // runtime.constructorOf(clazz)
- final val ENV_INFO = 316 // runtime.environmentInfo
- final val LINKING_INFO = 317 // runtime.linkingInfo
-
- final val THROW = 318 // <special-ops>.throw
-
-}
-
-class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
- import JSPrimitives._
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- private lazy val jsPrimitives: Map[Symbol, Int] = initJSPrimitives(ctx)
-
- override def getPrimitive(sym: Symbol): Int =
- jsPrimitives.getOrElse(sym, super.getPrimitive(sym))
-
- override def getPrimitive(app: Apply, tpe: Type)(implicit ctx: Context): Int =
- jsPrimitives.getOrElse(app.fun.symbol, super.getPrimitive(app, tpe))
-
- override def isPrimitive(fun: Tree): Boolean =
- jsPrimitives.contains(fun.symbol(ctx)) || super.isPrimitive(fun)
-
- /** Initialize the primitive map */
- private def initJSPrimitives(implicit ctx: Context): Map[Symbol, Int] = {
-
- val primitives = new mutable.HashMap[Symbol, Int]()
-
- // !!! Code duplicate with DottyPrimitives
- /** Add a primitive operation to the map */
- def addPrimitive(s: Symbol, code: Int): Unit = {
- assert(!(primitives contains s), "Duplicate primitive " + s)
- primitives(s) = code
- }
-
- def addPrimitives(cls: Symbol, method: TermName, code: Int)(implicit ctx: Context): Unit = {
- val alts = cls.info.member(method).alternatives.map(_.symbol)
- if (alts.isEmpty) {
- ctx.error(s"Unknown primitive method $cls.$method")
- } else {
- for (s <- alts)
- addPrimitive(s, code)
- }
- }
-
- val jsdefn = JSDefinitions.jsdefn
-
- addPrimitive(defn.Any_getClass, GETCLASS)
-
- for (i <- 0 to 22)
- addPrimitive(jsdefn.JSAny_fromFunction(i), F2JS)
- for (i <- 1 to 22)
- addPrimitive(jsdefn.JSThisFunction_fromFunction(i), F2JSTHIS)
-
- addPrimitive(jsdefn.JSDynamic_newInstance, DYNNEW)
-
- addPrimitive(jsdefn.JSDynamicLiteral_applyDynamicNamed, DYNLIT)
- addPrimitive(jsdefn.JSDynamicLiteral_applyDynamic, DYNLIT)
-
- addPrimitive(jsdefn.JSDictionary_delete, DICT_DEL)
-
- //addPrimitive(jsdefn.JSArray_create, ARR_CREATE)
-
- addPrimitive(jsdefn.JSPackage_typeOf, TYPEOF)
- addPrimitive(jsdefn.JSPackage_debugger, DEBUGGER)
- addPrimitive(jsdefn.JSPackage_native, JS_NATIVE)
-
- addPrimitive(jsdefn.JSObject_hasProperty, HASPROP)
- addPrimitive(jsdefn.JSObject_properties, OBJPROPS)
-
- addPrimitive(defn.BoxedUnit_UNIT, UNITVAL)
- //addPrimitive(defn.BoxedUnit_TYPE, UNITTYPE)
-
- //addPrimitive(jsdefn.Runtime_constructorOf, CONSTRUCTOROF)
- //addPrimitive(jsdefn.Runtime_environmentInfo, ENV_INFO)
- //addPrimitive(jsdefn.Runtime_linkingInfo, LINKING_INFO)
-
- addPrimitive(defn.throwMethod, THROW)
-
- primitives.toMap
- }
-
-}
diff --git a/src/dotty/tools/backend/sjs/ScopedVar.scala b/src/dotty/tools/backend/sjs/ScopedVar.scala
deleted file mode 100644
index 0e47f7b79..000000000
--- a/src/dotty/tools/backend/sjs/ScopedVar.scala
+++ /dev/null
@@ -1,38 +0,0 @@
-package dotty.tools.backend.sjs
-
-import language.implicitConversions
-
-class ScopedVar[A](init: A) {
- import ScopedVar.Assignment
-
- private var value = init
-
- def this()(implicit ev: Null <:< A) = this(ev(null))
-
- def get: A = value
- def :=(newValue: A): Assignment[A] = new Assignment(this, newValue)
-}
-
-object ScopedVar {
- class Assignment[T](scVar: ScopedVar[T], value: T) {
- private[ScopedVar] def push(): AssignmentStackElement[T] = {
- val stack = new AssignmentStackElement(scVar, scVar.value)
- scVar.value = value
- stack
- }
- }
-
- private class AssignmentStackElement[T](scVar: ScopedVar[T], oldValue: T) {
- private[ScopedVar] def pop(): Unit = {
- scVar.value = oldValue
- }
- }
-
- implicit def toValue[T](scVar: ScopedVar[T]): T = scVar.get
-
- def withScopedVars[T](ass: Assignment[_]*)(body: => T): T = {
- val stack = ass.map(_.push())
- try body
- finally stack.reverse.foreach(_.pop())
- }
-}
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 4bc5263e9..ad3249be2 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -17,7 +17,6 @@ import core.DenotTransformers.DenotTransformer
import core.Denotations.SingleDenotation
import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls}
-import dotty.tools.backend.sjs.GenSJSIR
/** The central class of the dotc compiler. The job of a compiler is to create
* runs, which process given `phases` in a given `rootContext`.
@@ -98,7 +97,6 @@ class Compiler {
new DropInlined, // Drop Inlined nodes, since backend has no use for them
new MoveStatics, // Move static methods to companion classes
new LabelDefs), // Converts calls to labels to jumps
- List(new GenSJSIR), // Generate .js code
List(new GenBCode) // Generate JVM bytecode
)
@@ -117,20 +115,7 @@ class Compiler {
*/
def rootContext(implicit ctx: Context): Context = {
ctx.initialize()(ctx)
- val actualPhases = if (ctx.settings.scalajs.value) {
- // Remove phases that Scala.js does not want
- phases.mapConserve(_.filter {
- case _: FunctionalInterfaces => false
- case _ => true
- }).filter(_.nonEmpty)
- } else {
- // Remove Scala.js-related phases
- phases.mapConserve(_.filter {
- case _: GenSJSIR => false
- case _ => true
- }).filter(_.nonEmpty)
- }
- ctx.setPhasePlan(actualPhases)
+ ctx.setPhasePlan(phases)
val rootScope = new MutableScope
val bootstrap = ctx.fresh
.setPeriod(Period(nextRunId, FirstPhaseId))
diff --git a/src/dotty/tools/dotc/config/PathResolver.scala b/src/dotty/tools/dotc/config/PathResolver.scala
index 1f9ee7eec..14a44531a 100644
--- a/src/dotty/tools/dotc/config/PathResolver.scala
+++ b/src/dotty/tools/dotc/config/PathResolver.scala
@@ -46,18 +46,7 @@ object PathResolver {
def classPathEnv = envOrElse("CLASSPATH", "")
def sourcePathEnv = envOrElse("SOURCEPATH", "")
- def javaBootClassPath =
- propOrElse("sun.boot.class.path", searchForBootClasspath)
- .split(":")
- .filterNot { jar =>
- // let's blacklist locally compiled classes:
- jar.contains("/dotty/library/target/classes") ||
- jar.contains("/dotty/library/target/scala-2.11/classes") ||
- jar.contains("/dotty/interfaces/target/classes") ||
- jar.contains("/dotty/target/scala-2.11/classes") ||
- jar.contains("/dotty/target/classes")
- }
- .mkString(":")
+ def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath)
def javaExtDirs = propOrEmpty("java.ext.dirs")
def scalaHome = propOrEmpty("scala.home")
@@ -266,8 +255,15 @@ class PathResolver(implicit ctx: Context) {
def containers = Calculated.containers
lazy val result: JavaClassPath = {
- val (dottyJars, others) = containers.partition(_.name.contains("dotty"))
- val cp = new JavaClassPath((dottyJars ++ others).toIndexedSeq, context)
+ // Prioritize `dotty.jar` and `dotty-lib.jar` to shadow others
+ val (dottyJars, others) =
+ containers.partition(x => x.name.contains("dotty-lib.jar") || x.name.contains("dotty.jar"))
+ // Then any jars with `dotty` in the name - putting them before scala-library
+ val (dottyCp, remaining) =
+ others.partition(_.name.contains("dotty-"))
+
+ val cp = new JavaClassPath((dottyJars ++ dottyCp ++ remaining).toIndexedSeq, context)
+
if (settings.Ylogcp.value) {
Console.println("Classpath built from " + settings.toConciseString(ctx.sstate))
Console.println("Defaults: " + PathResolver.Defaults)
diff --git a/src/dotty/tools/dotc/config/SJSPlatform.scala b/src/dotty/tools/dotc/config/SJSPlatform.scala
deleted file mode 100644
index 3ec8049ae..000000000
--- a/src/dotty/tools/dotc/config/SJSPlatform.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package dotty.tools.dotc.config
-
-import dotty.tools.dotc.core._
-import Contexts._
-import Symbols._
-
-import dotty.tools.backend.sjs.JSDefinitions
-
-class SJSPlatform()(implicit ctx: Context) extends JavaPlatform {
-
- /** Scala.js-specific definitions. */
- val jsDefinitions: JSDefinitions = new JSDefinitions()
-
- /** Is the SAMType `cls` also a SAM under the rules of the Scala.js back-end? */
- override def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
- defn.isFunctionClass(cls) || jsDefinitions.isJSFunctionClass(cls)
-
-}
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index edc68588d..639c4d111 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -27,7 +27,7 @@ import reporting._
import collection.mutable
import collection.immutable.BitSet
import printing._
-import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform}
+import config.{Settings, ScalaSettings, Platform, JavaPlatform}
import language.implicitConversions
import DenotTransformers.DenotTransformer
import util.Property.Key
@@ -550,8 +550,7 @@ object Contexts {
}
protected def newPlatform(implicit ctx: Context): Platform =
- if (settings.scalajs.value) new SJSPlatform
- else new JavaPlatform
+ new JavaPlatform
/** The loader that loads the members of _root_ */
def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root)