Skip to content

[WIP] Resurrect the Scala.js backend #3481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class JSCodeGen()(implicit ctx: Context) {
/* Finally, we emit true code for the remaining class defs. */
for (td <- allTypeDefs) {
val sym = td.symbol
implicit val pos = sym.pos
implicit val pos: Position = sym.pos

/* Do not actually emit code for primitive types nor scala.Array. */
val isPrimitive =
Expand Down Expand Up @@ -185,7 +185,7 @@ class JSCodeGen()(implicit ctx: Context) {
import dotty.tools.io._

val outputDirectory: AbstractFile = // TODO Support virtual files
new PlainDirectory(new Directory(new java.io.File(ctx.settings.d.value)))
new PlainDirectory(ctx.settings.outputDir.value)

val pathParts = sym.fullName.toString.split("[./]")
val dir = (outputDirectory /: pathParts.init)(_.subdirectoryNamed(_))
Expand All @@ -203,7 +203,7 @@ class JSCodeGen()(implicit ctx: Context) {
*/
private def genScalaClass(td: TypeDef): js.ClassDef = {
val sym = td.symbol.asClass
implicit val pos = sym.pos
implicit val pos: Position = sym.pos

assert(!sym.is(Trait),
"genScalaClass() must be called only for normal classes: "+sym)
Expand Down Expand Up @@ -285,9 +285,9 @@ class JSCodeGen()(implicit ctx: Context) {
js.ModuleExportDef("hello.world"),
js.MethodDef(static = false, js.StringLiteral("main"),
Nil, jstpe.AnyType,
js.Block(List(
Some(js.Block(List(
js.Apply(js.This()(jstpe.ClassType(classIdent.name)), js.Ident("main__V"), Nil)(jstpe.NoType),
js.Undefined())))(
js.Undefined()))))(
OptimizerHints.empty, None))
} else {
/*
Expand Down Expand Up @@ -336,20 +336,25 @@ class JSCodeGen()(implicit ctx: Context) {
*/
private def genRawJSClassData(td: TypeDef): js.ClassDef = {
val sym = td.symbol.asClass
implicit val pos = sym.pos
implicit val pos: Position = sym.pos

val classIdent = encodeClassFullNameIdent(sym)
val kind = {
if (sym.is(Trait)) ClassKind.AbstractJSType
else if (sym.is(ModuleClass)) ClassKind.NativeJSModuleClass
else ClassKind.NativeJSClass
}
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))
val jsNativeLoadSpec =
if (sym.is(Trait)) None
else Some(js.JSNativeLoadSpec.Global(fullJSNameOf(sym).split('.').toList))

js.ClassDef(classIdent, ClassKind.RawJSType,
js.ClassDef(classIdent, kind,
superClass,
genClassInterfaces(sym),
jsName,
jsNativeLoadSpec,
Nil)(
OptimizerHints.empty)
}
Expand All @@ -358,7 +363,7 @@ class JSCodeGen()(implicit ctx: Context) {
*/
private def genInterface(td: TypeDef): js.ClassDef = {
val sym = td.symbol.asClass
implicit val pos = sym.pos
implicit val pos: Position = sym.pos

val classIdent = encodeClassFullNameIdent(sym)

Expand Down Expand Up @@ -404,10 +409,7 @@ class JSCodeGen()(implicit ctx: Context) {
"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 {
classSym.info.decls.filter(f => !f.is(Method) && f.isTerm).map({ f =>
implicit val pos = f.pos

val name =
Expand Down Expand Up @@ -451,7 +453,7 @@ class JSCodeGen()(implicit ctx: Context) {
}
}*/

js.FieldDef(name, irTpe, f.is(Mutable))
js.FieldDef(static = false, name, irTpe, f.is(Mutable))
}).toList
}

Expand Down Expand Up @@ -510,7 +512,7 @@ class JSCodeGen()(implicit ctx: Context) {
None
} else*/ if (sym.is(Deferred)) {
Some(js.MethodDef(static = false, methodName,
jsParams, toIRType(patchedResultType(sym)), js.EmptyTree)(
jsParams, toIRType(patchedResultType(sym)), None)(
OptimizerHints.empty, None))
} else /*if (isJSNativeCtorDefaultParam(sym)) {
None
Expand Down Expand Up @@ -549,7 +551,7 @@ class JSCodeGen()(implicit ctx: Context) {
} else*/ if (sym.isConstructor) {
js.MethodDef(static = false, methodName,
jsParams, jstpe.NoType,
genStat(rhs))(optimizerHints, None)
Some(genStat(rhs)))(optimizerHints, None)
} else {
val resultIRType = toIRType(patchedResultType(sym))
genMethodDef(static = false, methodName,
Expand All @@ -576,7 +578,7 @@ class JSCodeGen()(implicit ctx: Context) {
tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = {
implicit val pos = tree.pos

ctx.debuglog("genMethod " + methodName.name)
ctx.debuglog("genMethod " + methodName.encodedName)
ctx.debuglog("")

val jsParams = for (param <- paramsSyms) yield {
Expand All @@ -590,7 +592,7 @@ class JSCodeGen()(implicit ctx: Context) {
else genExpr(tree)

//if (!isScalaJSDefinedJSClass(currentClassSym)) {
js.MethodDef(static, methodName, jsParams, resultIRType, genBody())(
js.MethodDef(static, methodName, jsParams, resultIRType, Some(genBody()))(
optimizerHints, None)
/*} else {
assert(!static, tree.pos)
Expand Down Expand Up @@ -1021,7 +1023,7 @@ class JSCodeGen()(implicit ctx: Context) {

/** Gen JS code for a primitive method call. */
private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = {
import scala.tools.nsc.backend.ScalaPrimitives._
import scala.tools.nsc.backend.ScalaPrimitivesOps._

implicit val pos = tree.pos

Expand Down Expand Up @@ -1061,7 +1063,7 @@ class JSCodeGen()(implicit ctx: Context) {

/** 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._
import scala.tools.nsc.backend.ScalaPrimitivesOps._

implicit val pos = tree.pos

Expand Down Expand Up @@ -1102,7 +1104,7 @@ class JSCodeGen()(implicit ctx: Context) {

/** 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 scala.tools.nsc.backend.ScalaPrimitivesOps._
import js.UnaryOp._

/* Codes for operation types, in an object so that they can be 'final val'
Expand Down Expand Up @@ -1280,7 +1282,7 @@ class JSCodeGen()(implicit ctx: Context) {
private def genUniversalEqualityOp(lhs: Tree, rhs: Tree, code: Int)(
implicit pos: Position): js.Tree = {

import scala.tools.nsc.backend.ScalaPrimitives._
import scala.tools.nsc.backend.ScalaPrimitivesOps._

val genLhs = genExpr(lhs)
val genRhs = genExpr(rhs)
Expand Down Expand Up @@ -1409,7 +1411,7 @@ class JSCodeGen()(implicit ctx: Context) {

/** 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._
import scala.tools.nsc.backend.ScalaPrimitivesOps._

implicit val pos = tree.pos

Expand All @@ -1423,8 +1425,9 @@ class JSCodeGen()(implicit ctx: Context) {
case defn.ArrayOf(el) => el
case JavaArrayType(el) => el
case tpe =>
ctx.error(s"expected Array $tpe")
ErrorType
val msg = ex"expected Array $tpe"
ctx.error(msg)
ErrorType(msg)
}

def genSelect(): js.Tree =
Expand Down Expand Up @@ -1472,7 +1475,7 @@ class JSCodeGen()(implicit ctx: Context) {

/** Gen JS code for a coercion */
private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = {
import scala.tools.nsc.backend.ScalaPrimitives._
import scala.tools.nsc.backend.ScalaPrimitivesOps._

implicit val pos = tree.pos

Expand Down Expand Up @@ -1881,7 +1884,7 @@ class JSCodeGen()(implicit ctx: Context) {
}.unzip

val formalParamNames = sym.info.paramNamess.flatten.drop(envSize)
val formalParamTypes = sym.info.paramTypess.flatten.drop(envSize)
val formalParamTypes = sym.info.paramInfoss.flatten.drop(envSize)
val (formalParams, actualParams) = formalParamNames.zip(formalParamTypes).map {
case (name, tpe) =>
val formalParam = js.ParamDef(freshLocalIdent(name.toString),
Expand Down Expand Up @@ -2175,7 +2178,7 @@ class JSCodeGen()(implicit ctx: Context) {
implicit pos: Position): List[js.Tree] = {

def paramNamesAndTypes(implicit ctx: Context): List[(Names.TermName, Type)] =
sym.info.paramNamess.flatten.zip(sym.info.paramTypess.flatten)
sym.info.paramNamess.flatten.zip(sym.info.paramInfoss.flatten)

val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx =>
for ((name, tpe) <- paramNamesAndTypes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,15 @@ final class JSDefinitions()(implicit ctx: Context) {
*
* This is similar to `isVarArityClass` in `Definitions.scala`.
*/
private def isScalaJSVarArityClass(cls: Symbol, prefix: Name): Boolean = {
private def isScalaJSVarArityClass(cls: Symbol, prefix: String): Boolean = {
val name = scalajsClassName(cls)
name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
name.startsWith(prefix) && name.toString.drop(prefix.length).forall(_.isDigit)
}

def isJSFunctionClass(cls: Symbol): Boolean =
isScalaJSVarArityClass(cls, nme.Function)

private val ThisFunctionName = termName("ThisFunction")
isScalaJSVarArityClass(cls, str.Function)

def isJSThisFunctionClass(cls: Symbol): Boolean =
isScalaJSVarArityClass(cls, ThisFunctionName)
isScalaJSVarArityClass(cls, "ThisFunction")

}
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ object JSEncoding {
}

def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean =
sym.name.isImplClassName
sym.name.endsWith(nme.IMPL_CLASS_SUFFIX.toString)

def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = {
if (sym == defn.ObjectClass) jstpe.AnyType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Flags._
import Symbols._
import NameOps._
import StdNames._
import NameKinds.DefaultGetterName

import JSDefinitions._

Expand Down Expand Up @@ -66,11 +67,17 @@ object JSInterop {
* is a JS type.
*/
def isJSDefaultParam(sym: Symbol)(implicit ctx: Context): Boolean = {
sym.name.isDefaultGetterName && {
sym.name.is(DefaultGetterName) && {
val owner = sym.owner
if (owner.is(ModuleClass) &&
sym.name.asTermName.defaultGetterToMethod == nme.CONSTRUCTOR) {
isJSType(owner.linkedClass)
if (owner.is(ModuleClass)) {
val isConstructor = sym.name match {
case DefaultGetterName(methName, _) => methName == nme.CONSTRUCTOR
case _ => false
}
if (isConstructor)
isJSType(owner.linkedClass)
else
isJSType(owner)
} else {
isJSType(owner)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ object JSPrimitives {

class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
import JSPrimitives._
import scala.tools.nsc.backend.ScalaPrimitives._
import scala.tools.nsc.backend.ScalaPrimitivesOps._

private lazy val jsPrimitives: Map[Symbol, Int] = initJSPrimitives(ctx)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import language.implicitConversions
class ScopedVar[A](init: A) {
import ScopedVar.Assignment

private[this] var value = init
private[ScopedVar] var value = init

def this()(implicit ev: Null <:< A) = this(ev(null))

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import util.FreshNameCreator
import core.DenotTransformers.DenotTransformer
import core.Denotations.SingleDenotation

import dotty.tools.backend.sjs
import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls}
import dotty.tools.dotc.transform.localopt.Simplify

Expand Down Expand Up @@ -109,6 +110,7 @@ class Compiler {
new CollectSuperCalls, // Find classes that are called with super
new DropInlined, // Drop Inlined nodes, since backend has no use for them
new LabelDefs), // Converts calls to labels to jumps
List(new sjs.GenSJSIR), // Generate .js code (not enabled by default)
List(new GenBCode) // Generate JVM bytecode
)

Expand Down
19 changes: 18 additions & 1 deletion compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import printing.XprintMode
import scala.annotation.tailrec
import dotty.tools.io.VirtualFile
import scala.util.control.NonFatal
import dotty.tools.backend.sjs

/** A compiler run. Exports various methods to compile source files */
class Run(comp: Compiler, ictx: Context) {
Expand All @@ -38,7 +39,23 @@ class Run(comp: Compiler, ictx: Context) {
*/
protected[this] def rootContext(implicit ctx: Context): Context = {
ctx.initialize()(ctx)
ctx.setPhasePlan(comp.phases)

val actualPhases = if (ctx.settings.scalajs.value) {
// Remove phases that Scala.js does not want
comp.phases.mapConserve(_.filter {
case _: transform.FunctionalInterfaces => false
case _ => true
}).filter(_.nonEmpty)
} else {
// Remove Scala.js-related phases
comp.phases.mapConserve(_.filter {
case _: sjs.GenSJSIR => false
case _ => true
}).filter(_.nonEmpty)
}

ctx.setPhasePlan(actualPhases)

val rootScope = new MutableScope
val bootstrap = ctx.fresh
.setPeriod(Period(comp.nextRunId, FirstPhaseId))
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ScalaSettings extends Settings.SettingGroup {
val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/)
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.")
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import reporting.diagnostic.Message
import collection.mutable
import collection.immutable.BitSet
import printing._
import config.{Settings, ScalaSettings, Platform, JavaPlatform}
import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform}
import language.implicitConversions
import DenotTransformers.DenotTransformer
import util.Property.Key
Expand Down Expand Up @@ -547,7 +547,8 @@ object Contexts {
}

protected def newPlatform(implicit ctx: Context): Platform =
new JavaPlatform
if (settings.scalajs.value) new SJSPlatform
else new JavaPlatform

/** The loader that loads the members of _root_ */
def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root)
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/transform/GetClass.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ class GetClass extends MiniPhase {

override def phaseName: String = "getClass"

// getClass transformation should be applied to specialized methods
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure], classOf[FunctionalInterfaces])
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure])

override def transformApply(tree: Apply)(implicit ctx: Context): Tree = {
import ast.Trees._
Expand Down
Loading