aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Doeraene <sjrdoeraene@gmail.com>2016-02-26 16:30:39 +0100
committerSébastien Doeraene <sjrdoeraene@gmail.com>2016-03-01 15:47:56 +0100
commitcd4004a82d9713bbb0b120aba83b3ed8fc9f1372 (patch)
tree309f090cb79fba02746b7b8a0109e171881d5d27
parent0ebf36b394b00f3f432d8fdedeaa15e7d4df2b06 (diff)
downloaddotty-cd4004a82d9713bbb0b120aba83b3ed8fc9f1372.tar.gz
dotty-cd4004a82d9713bbb0b120aba83b3ed8fc9f1372.tar.bz2
dotty-cd4004a82d9713bbb0b120aba83b3ed8fc9f1372.zip
Initial infrastructure and hello world for the Scala.js back-end.
The Scala.js back-end can be enabled with the `-scalajs` command-line option. Currently, it adds one phase to the pipeline, which emits .sjsir files from trees. A sandbox project `sjsSandbox`, in `sandbox/scalajs/`, can be used to easily test Scala.js compilation. One can run the `main()` method of the `hello.world` object with > sjsSandbox/run The back-end only contains the bare mimimum to compile the hello world application in the sandbox. Anything else will blow up (for example, primitive method calls). It is a work-in-progress.
-rw-r--r--project/Build.scala151
-rw-r--r--project/plugins.sbt2
-rw-r--r--sandbox/scalajs/hello.scala15
-rw-r--r--src/dotty/tools/backend/sjs/GenSJSIR.scala14
-rw-r--r--src/dotty/tools/backend/sjs/JSCodeGen.scala1180
-rw-r--r--src/dotty/tools/backend/sjs/JSDefinitions.scala185
-rw-r--r--src/dotty/tools/backend/sjs/JSEncoding.scala376
-rw-r--r--src/dotty/tools/backend/sjs/JSPositions.scala65
-rw-r--r--src/dotty/tools/backend/sjs/JSPrimitives.scala113
-rw-r--r--src/dotty/tools/backend/sjs/ScopedVar.scala38
-rw-r--r--src/dotty/tools/dotc/Compiler.scala13
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
12 files changed, 2152 insertions, 1 deletions
diff --git a/project/Build.scala b/project/Build.scala
index 66a380e96..4a22f3d82 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -4,6 +4,9 @@ import java.io.{ RandomAccessFile, File }
import java.nio.channels.FileLock
import scala.reflect.io.Path
+import org.scalajs.sbtplugin.ScalaJSPlugin
+import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
+
object DottyBuild extends Build {
val jenkinsMemLimit = List("-Xmx1300m")
@@ -33,6 +36,10 @@ object DottyBuild extends Build {
)
}
+ /** Enforce 2.11.5. Do not let it be upgraded by dependencies. */
+ private val overrideScalaVersionSetting =
+ ivyScala := ivyScala.value.map(_.copy(overrideScalaVersion = true))
+
lazy val `dotty-interfaces` = project.in(file("interfaces")).
settings(
// Do not append Scala versions to the generated artifacts
@@ -44,6 +51,8 @@ object DottyBuild extends Build {
lazy val dotty = project.in(file(".")).
dependsOn(`dotty-interfaces`).
settings(
+ overrideScalaVersionSetting,
+
// set sources to src/, tests to test/ and resources to resources/
scalaSource in Compile := baseDirectory.value / "src",
javaSource in Compile := baseDirectory.value / "src",
@@ -74,6 +83,8 @@ object DottyBuild extends Build {
"org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test",
"com.novocode" % "junit-interface" % "0.11" % "test",
"jline" % "jline" % "2.12"),
+ libraryDependencies +=
+ "org.scala-js" %% "scalajs-ir" % scalaJSVersion,
// enable improved incremental compilation algorithm
incOptions := incOptions.value.withNameHashing(true),
@@ -144,9 +155,54 @@ object DottyBuild extends Build {
addCommandAlias("partest-only-no-bootstrap", ";test:package;package; lockPartestFile;test:test-only dotc.tests;runPartestRunner")
)
+ /** A sandbox to play with the Scala.js back-end of dotty.
+ *
+ * This sandbox is compiled with dotty with support for Scala.js. It can be
+ * used like any regular Scala.js project. In particular, `fastOptJS` will
+ * produce a .js file, and `run` will run the JavaScript code with a JS VM.
+ *
+ * Simply running `dotty/run -scalajs` without this sandbox is not very
+ * useful, as that would not provide the linker and JS runners.
+ */
+ lazy val sjsSandbox = project.in(file("sandbox/scalajs")).
+ enablePlugins(ScalaJSPlugin).
+ settings(
+ overrideScalaVersionSetting,
+
+ /* Remove the Scala.js compiler plugin for scalac, and enable the
+ * Scala.js back-end of dotty instead.
+ */
+ libraryDependencies ~= { deps =>
+ deps.filterNot(_.name.startsWith("scalajs-compiler"))
+ },
+ scalacOptions += "-scalajs",
+
+ // The main class cannot be found automatically due to the empty inc.Analysis
+ mainClass in Compile := Some("hello.world"),
+
+ // While developing the Scala.js back-end, it is very useful to see the trees dotc gives us
+ scalacOptions += "-Xprint:labelDef",
+
+ /* Debug-friendly Scala.js optimizer options.
+ * In particular, typecheck the Scala.js IR found on the classpath.
+ */
+ scalaJSOptimizerOptions ~= {
+ _.withCheckScalaJSIR(true).withParallel(false)
+ }
+ ).
+ settings(compileWithDottySettings).
+ settings(inConfig(Compile)(Seq(
+ /* Make sure jsDependencyManifest runs after compile, otherwise compile
+ * might remove the entire directory afterwards.
+ */
+ jsDependencyManifest <<= jsDependencyManifest.dependsOn(compile)
+ )))
+
lazy val `dotty-bench` = project.in(file("bench")).
dependsOn(dotty % "compile->test").
settings(
+ overrideScalaVersionSetting,
+
baseDirectory in (Test,run) := (baseDirectory in dotty).value,
libraryDependencies ++= Seq("com.storm-enroute" %% "scalameter" % "0.6" % Test,
@@ -198,4 +254,99 @@ object DottyBuild extends Build {
})
case None => throw new RuntimeException("ERROR: sbt getJarPaths: ivyHome not defined")
}
+
+ // Compile with dotty
+ lazy val compileWithDottySettings = {
+ inConfig(Compile)(inTask(compile)(Defaults.runnerTask) ++ Seq(
+ // Compile with dotty
+ fork in compile := true,
+
+ compile := {
+ val inputs = (compileInputs in compile).value
+ import inputs.config._
+
+ val s = streams.value
+ val logger = s.log
+ val cacheDir = s.cacheDirectory
+
+ // Discover classpaths
+
+ def cpToString(cp: Seq[File]) =
+ cp.map(_.getAbsolutePath).mkString(java.io.File.pathSeparator)
+
+ val compilerCp = Attributed.data((fullClasspath in (dotty, Compile)).value)
+ val cpStr = cpToString(classpath ++ compilerCp)
+
+ // List all my dependencies (recompile if any of these changes)
+
+ val allMyDependencies = classpath filterNot (_ == classesDirectory) flatMap { cpFile =>
+ if (cpFile.isDirectory) (cpFile ** "*.class").get
+ else Seq(cpFile)
+ }
+
+ // Compile
+
+ val cachedCompile = FileFunction.cached(cacheDir / "compile",
+ FilesInfo.lastModified, FilesInfo.exists) { dependencies =>
+
+ logger.info(
+ "Compiling %d Scala sources to %s..." format (
+ sources.size, classesDirectory))
+
+ if (classesDirectory.exists)
+ IO.delete(classesDirectory)
+ IO.createDirectory(classesDirectory)
+
+ val sourcesArgs = sources.map(_.getAbsolutePath()).toList
+
+ /* run.run() below in doCompile() will emit a call to its
+ * logger.info("Running dotty.tools.dotc.Main [...]")
+ * which we do not want to see. We use this patched logger to
+ * filter out that particular message.
+ */
+ val patchedLogger = new Logger {
+ def log(level: Level.Value, message: => String) = {
+ val msg = message
+ if (level != Level.Info ||
+ !msg.startsWith("Running dotty.tools.dotc.Main"))
+ logger.log(level, msg)
+ }
+ def success(message: => String) = logger.success(message)
+ def trace(t: => Throwable) = logger.trace(t)
+ }
+
+ def doCompile(sourcesArgs: List[String]): Unit = {
+ val run = (runner in compile).value
+ run.run("dotty.tools.dotc.Main", compilerCp,
+ "-classpath" :: cpStr ::
+ "-d" :: classesDirectory.getAbsolutePath() ::
+ options ++:
+ sourcesArgs,
+ patchedLogger) foreach sys.error
+ }
+
+ // Work around the Windows limitation on command line length.
+ val isWindows =
+ System.getProperty("os.name").toLowerCase().indexOf("win") >= 0
+ if ((fork in compile).value && isWindows &&
+ (sourcesArgs.map(_.length).sum > 1536)) {
+ IO.withTemporaryFile("sourcesargs", ".txt") { sourceListFile =>
+ IO.writeLines(sourceListFile, sourcesArgs)
+ doCompile(List("@"+sourceListFile.getAbsolutePath()))
+ }
+ } else {
+ doCompile(sourcesArgs)
+ }
+
+ // Output is all files in classesDirectory
+ (classesDirectory ** AllPassFilter).get.toSet
+ }
+
+ cachedCompile((sources ++ allMyDependencies).toSet)
+
+ // We do not have dependency analysis when compiling externally
+ sbt.inc.Analysis.Empty
+ }
+ ))
+ }
}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index f9c0dbf1a..0c8b7737e 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -6,3 +6,5 @@
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")
+
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7")
diff --git a/sandbox/scalajs/hello.scala b/sandbox/scalajs/hello.scala
new file mode 100644
index 000000000..bd4aa8cc5
--- /dev/null
+++ b/sandbox/scalajs/hello.scala
@@ -0,0 +1,15 @@
+package hello
+
+import scala.scalajs.js
+
+trait MyTrait {
+ val x = 5
+ def foo(y: Int) = x
+}
+
+object world extends js.JSApp with MyTrait {
+ def main(): Unit = {
+ println("hello dotty.js!")
+ println(foo(4))
+ }
+}
diff --git a/src/dotty/tools/backend/sjs/GenSJSIR.scala b/src/dotty/tools/backend/sjs/GenSJSIR.scala
new file mode 100644
index 000000000..819a8f0e3
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/GenSJSIR.scala
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 000000000..be4e56375
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/JSCodeGen.scala
@@ -0,0 +1,1180 @@
+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 Flags._
+import dotty.tools.dotc.ast.Trees._
+import Types._
+import Symbols._
+import Denotations._
+import Phases._
+import dotty.tools.dotc.util.Positions
+import Positions.Position
+import StdNames._
+
+import dotty.tools.dotc.transform.Erasure
+
+import org.scalajs.core.ir
+import org.scalajs.core.ir.{ClassKind, Trees => js, Types => jstpe}
+import js.OptimizerHints
+
+import JSEncoding._
+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)
+
+ // 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 (isRawJSType(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 = {
+ ???
+ }
+
+ /** 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)
+ }
+ }
+
+ /* Work around https://github.com/scala-js/scala-js/issues/2259
+ * TODO Remove this when we upgrade to Scala.js 0.6.8.
+ */
+ val methodDef1 = if (!sym.owner.is(Trait)) {
+ methodDef
+ } else {
+ val workaroundBody = js.Block(
+ js.Apply(js.ClassOf(jstpe.ClassType(encodeClassFullName(sym.owner))),
+ js.Ident("isPrimitive__Z"), Nil)(jstpe.BooleanType),
+ methodDef.body)
+ methodDef.copy(body = workaroundBody)(
+ methodDef.optimizerHints, methodDef.hash)
+ }
+
+ Some(methodDef1)
+ }
+ }
+ }
+
+ /** 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: ir.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 Throw(expr) =>
+ val ex = genExpr(expr)
+ js.Throw {
+ if (isMaybeJavaScriptException(expr.tpe)) {
+ genApplyMethod(
+ genLoadModule(RuntimePackageModule),
+ Runtime_unwrapJavaScriptException,
+ List(ex))
+ } else {
+ ex
+ }
+ }*/
+
+ case app: Apply =>
+ genApply(app, isStat)
+
+ /*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.isStaticMember) {
+ genStaticMember(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 av: ArrayValue =>
+ genArrayValue(av)
+
+ /** A Match reaching the backend is supposed to be optimized as a switch */
+ case mtch: Match =>
+ genMatch(mtch, isStat)
+
+ /** Anonymous function (only with -Ydelambdafy:method) */
+ case fun: Function =>
+ genAnonFunction(fun)
+
+ case EmptyTree =>
+ js.Skip()*/
+
+ case _ =>
+ throw new FatalError("Unexpected tree in genExpr: " +
+ tree + "/" + tree.getClass + " at: " + tree.pos)
+ }
+ } // 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
+ }
+ }
+
+ /** 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
+ }
+
+ def isRawJSDefaultParam: Boolean = {
+ false /*
+ if (isCtorDefaultParam(sym)) {
+ isRawJSCtorDefaultParam(sym)
+ } else {
+ sym.hasFlag(reflect.internal.Flags.DEFAULTPARAM) &&
+ isRawJSType(sym.owner.tpe)
+ }*/
+ }
+
+ fun match {
+ /*case _: TypeApply =>
+ genApplyTypeApply(tree)*/
+
+ /*case _ if isRawJSDefaultParam =>
+ js.UndefinedParam()(toIRType(sym.tpe.resultType))*/
+
+ 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 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) {
+ genStringCall(tree)
+ } else if (isRawJSType(sym.owner)) {
+ if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym))
+ genPrimitiveJSCall(tree, 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 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 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)
+ }
+ }
+ }*/
+ }
+
+ /** Generate loading of a module value
+ * 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
+
+ //val isGlobalScope = sym.tpe.typeSymbol isSubClass JSGlobalScopeClass
+
+ /*if (isGlobalScope) {
+ genLoadGlobal()
+ } else if (isJSNativeClass(sym)) {
+ genPrimitiveJSModule(sym)
+ } else {*/
+ val cls = jstpe.ClassType(encodeClassFullName(sym))
+ if (isRawJSType(sym)) js.LoadJSModule(cls)
+ else js.LoadModule(cls)
+ //}
+ }
+
+ /** 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
+
+}
diff --git a/src/dotty/tools/backend/sjs/JSDefinitions.scala b/src/dotty/tools/backend/sjs/JSDefinitions.scala
new file mode 100644
index 000000000..1ee560b35
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/JSDefinitions.scala
@@ -0,0 +1,185 @@
+package dotty.tools.backend.sjs
+
+import dotty.tools.dotc.core._
+
+import Types._
+import Contexts._
+import Symbols._
+import StdNames._
+import Decorators._
+
+object JSDefinitions {
+ @dotty.tools.sharable
+ private val cache = new java.util.WeakHashMap[ContextBase, JSDefinitions]
+
+ // TODO Figure out where best to define this
+ def jsdefn(implicit ctx: Context): JSDefinitions = cache.synchronized {
+ val baseCtx = ctx.base
+ val cached = cache.get(baseCtx)
+ if (cached != null) {
+ cached
+ } else {
+ val newJSDefinitions = new JSDefinitions()
+ cache.put(baseCtx, newJSDefinitions)
+ newJSDefinitions
+ }
+ }
+}
+
+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
+
+}
diff --git a/src/dotty/tools/backend/sjs/JSEncoding.scala b/src/dotty/tools/backend/sjs/JSEncoding.scala
new file mode 100644
index 000000000..b35be3264
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/JSEncoding.scala
@@ -0,0 +1,376 @@
+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._
+
+/** 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): (Symbol, js.Ident) = {
+ require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym)
+ require(sym.owner == defn.StringClass)
+ require(!sym.isClassConstructor && !sym.is(Flags.Private))
+
+ val (encodedName, paramsString) =
+ encodeMethodNameInternal(sym, inRTClass = true)
+ val methodIdent = js.Ident(encodedName + paramsString,
+ Some(sym.unexpandedName.decoded + paramsString))
+
+ (jsdefn.RuntimeStringModuleClass, methodIdent)
+ }
+
+ 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 (isRawJSType(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 =
+ ir.Definitions.encodeClassName(sym.fullName.toString)
+
+ private def encodeMemberNameInternal(sym: Symbol)(
+ implicit ctx: Context): String = {
+ sym.name.toString.replace("_", "$und")
+ }
+
+ def isRawJSType(sym: Symbol)(implicit ctx: Context): Boolean =
+ sym.hasAnnotation(jsdefn.RawJSTypeAnnot)
+
+ def isScalaJSDefinedJSClass(sym: Symbol)(implicit ctx: Context): Boolean =
+ isRawJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot)
+
+ 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 || isRawJSType(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/JSPositions.scala b/src/dotty/tools/backend/sjs/JSPositions.scala
new file mode 100644
index 000000000..10570da00
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/JSPositions.scala
@@ -0,0 +1,65 @@
+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
new file mode 100644
index 000000000..47d705fe8
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/JSPrimitives.scala
@@ -0,0 +1,113 @@
+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
+
+}
+
+class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
+ import JSPrimitives._
+
+ 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)
+
+ primitives.toMap
+ }
+
+}
diff --git a/src/dotty/tools/backend/sjs/ScopedVar.scala b/src/dotty/tools/backend/sjs/ScopedVar.scala
new file mode 100644
index 000000000..0e47f7b79
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/ScopedVar.scala
@@ -0,0 +1,38 @@
+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 d526903b8..2a3346486 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -16,6 +16,7 @@ import core.DenotTransformers.DenotTransformer
import core.Denotations.SingleDenotation
import dotty.tools.backend.jvm.{LabelDefs, GenBCode}
+import dotty.tools.backend.sjs.GenSJSIR
class Compiler {
@@ -83,6 +84,7 @@ class Compiler {
List(new ExpandPrivate,
new CollectEntryPoints,
new LabelDefs),
+ List(new GenSJSIR),
List(new GenBCode)
)
@@ -101,7 +103,16 @@ class Compiler {
*/
def rootContext(implicit ctx: Context): Context = {
ctx.definitions.init(ctx)
- ctx.setPhasePlan(phases)
+ val actualPhases = if (ctx.settings.scalajs.value) {
+ phases
+ } else {
+ // Remove Scala.js-related phases
+ phases.mapConserve(_.filter {
+ case _: GenSJSIR => false
+ case _ => true
+ }).filter(_.nonEmpty)
+ }
+ ctx.setPhasePlan(actualPhases)
val rootScope = new MutableScope
val bootstrap = ctx.fresh
.setPeriod(Period(nextRunId, FirstPhaseId))
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index 65bc9ba23..aa264329c 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -29,6 +29,7 @@ class ScalaSettings extends Settings.SettingGroup {
val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"),
"jvm-1.8")
+ val scalajs = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).")
val unchecked = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
val uniqid = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.")
val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.")