Skip to content

Commit e68a6bb

Browse files
committed
Resurrect the Scala.js backend
This partially reverts 06a3d47 to bring back Dotty.js from the dead. Many things are broken still, but this is enough to do some experiments: - The backend was initially removed because of scala#1574, I don't see why GetClass has a requirement on FunctionalInterfaces, so I just removed it ¯\_(ツ)_/¯ - Scala.js was upgraded to 0.6.19. More recent versions of Scala.js cannot be used because they require sbt 0.13.16 and we're blocked by sbt/sbt#3460, this won't be an issue anymore once we switch to sbt 1 (see scala#3441) - GenSJSIR was hacked to compile with 0.6.19 but is missing most of the changes to the Scala.js backend phase for Scala 2 that happened since it was initially ported to Dotty from Scala.js 0.6.8 - scalajs-ir does not compile with Dotty anymore, see comments in Build.scala - The Scala.js backend explodes when extending a Scala 2 trait because of the way LinkScala2Impls transforms supercalls to these traits. Luckily, we don't need to extend js.JSApp anymore to make a Hello World :P. I verified that the following works: sbt sjsSandbox/run But I don't know if anything else does!
1 parent 7759e00 commit e68a6bb

17 files changed

+115
-79
lines changed

compiler/sjs/backend/sjs/JSCodeGen.scala renamed to compiler/src/dotty/tools/backend/backend/sjs/JSCodeGen.scala

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class JSCodeGen()(implicit ctx: Context) {
185185
import dotty.tools.io._
186186

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

190190
val pathParts = sym.fullName.toString.split("[./]")
191191
val dir = (outputDirectory /: pathParts.init)(_.subdirectoryNamed(_))
@@ -285,9 +285,9 @@ class JSCodeGen()(implicit ctx: Context) {
285285
js.ModuleExportDef("hello.world"),
286286
js.MethodDef(static = false, js.StringLiteral("main"),
287287
Nil, jstpe.AnyType,
288-
js.Block(List(
288+
Some(js.Block(List(
289289
js.Apply(js.This()(jstpe.ClassType(classIdent.name)), js.Ident("main__V"), Nil)(jstpe.NoType),
290-
js.Undefined())))(
290+
js.Undefined()))))(
291291
OptimizerHints.empty, None))
292292
} else {
293293
/*
@@ -339,17 +339,22 @@ class JSCodeGen()(implicit ctx: Context) {
339339
implicit val pos = sym.pos
340340

341341
val classIdent = encodeClassFullNameIdent(sym)
342+
val kind = {
343+
if (sym.is(Trait)) ClassKind.AbstractJSType
344+
else if (sym.is(ModuleClass)) ClassKind.NativeJSModuleClass
345+
else ClassKind.NativeJSClass
346+
}
342347
val superClass =
343348
if (sym.is(Trait)) None
344349
else Some(encodeClassFullNameIdent(sym.superClass))
345-
val jsName =
346-
if (sym.is(Trait) || sym.is(ModuleClass)) None
347-
else Some(fullJSNameOf(sym))
350+
val jsNativeLoadSpec =
351+
if (sym.is(Trait)) None
352+
else Some(js.JSNativeLoadSpec.Global(fullJSNameOf(sym).split('.').toList))
348353

349-
js.ClassDef(classIdent, ClassKind.RawJSType,
354+
js.ClassDef(classIdent, kind,
350355
superClass,
351356
genClassInterfaces(sym),
352-
jsName,
357+
jsNativeLoadSpec,
353358
Nil)(
354359
OptimizerHints.empty)
355360
}
@@ -404,10 +409,7 @@ class JSCodeGen()(implicit ctx: Context) {
404409
"genClassFields called with a ClassDef other than the current one")
405410

406411
// Non-method term members are fields
407-
(for {
408-
f <- classSym.info.decls
409-
if !f.is(Method) && f.isTerm
410-
} yield {
412+
classSym.info.decls.filter(f => !f.is(Method) && f.isTerm).map({ f =>
411413
implicit val pos = f.pos
412414

413415
val name =
@@ -451,7 +453,7 @@ class JSCodeGen()(implicit ctx: Context) {
451453
}
452454
}*/
453455

454-
js.FieldDef(name, irTpe, f.is(Mutable))
456+
js.FieldDef(static = false, name, irTpe, f.is(Mutable))
455457
}).toList
456458
}
457459

@@ -510,7 +512,7 @@ class JSCodeGen()(implicit ctx: Context) {
510512
None
511513
} else*/ if (sym.is(Deferred)) {
512514
Some(js.MethodDef(static = false, methodName,
513-
jsParams, toIRType(patchedResultType(sym)), js.EmptyTree)(
515+
jsParams, toIRType(patchedResultType(sym)), None)(
514516
OptimizerHints.empty, None))
515517
} else /*if (isJSNativeCtorDefaultParam(sym)) {
516518
None
@@ -549,7 +551,7 @@ class JSCodeGen()(implicit ctx: Context) {
549551
} else*/ if (sym.isConstructor) {
550552
js.MethodDef(static = false, methodName,
551553
jsParams, jstpe.NoType,
552-
genStat(rhs))(optimizerHints, None)
554+
Some(genStat(rhs)))(optimizerHints, None)
553555
} else {
554556
val resultIRType = toIRType(patchedResultType(sym))
555557
genMethodDef(static = false, methodName,
@@ -576,7 +578,7 @@ class JSCodeGen()(implicit ctx: Context) {
576578
tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = {
577579
implicit val pos = tree.pos
578580

579-
ctx.debuglog("genMethod " + methodName.name)
581+
ctx.debuglog("genMethod " + methodName.encodedName)
580582
ctx.debuglog("")
581583

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

592594
//if (!isScalaJSDefinedJSClass(currentClassSym)) {
593-
js.MethodDef(static, methodName, jsParams, resultIRType, genBody())(
595+
js.MethodDef(static, methodName, jsParams, resultIRType, Some(genBody()))(
594596
optimizerHints, None)
595597
/*} else {
596598
assert(!static, tree.pos)
@@ -1021,7 +1023,7 @@ class JSCodeGen()(implicit ctx: Context) {
10211023

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

10261028
implicit val pos = tree.pos
10271029

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

10621064
/** Gen JS code for a simple unary operation. */
10631065
private def genSimpleUnaryOp(tree: Apply, arg: Tree, code: Int): js.Tree = {
1064-
import scala.tools.nsc.backend.ScalaPrimitives._
1066+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
10651067

10661068
implicit val pos = tree.pos
10671069

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

11031105
/** Gen JS code for a simple binary operation. */
11041106
private def genSimpleBinaryOp(tree: Apply, lhs: Tree, rhs: Tree, code: Int): js.Tree = {
1105-
import scala.tools.nsc.backend.ScalaPrimitives._
1107+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
11061108
import js.UnaryOp._
11071109

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

1283-
import scala.tools.nsc.backend.ScalaPrimitives._
1285+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
12841286

12851287
val genLhs = genExpr(lhs)
12861288
val genRhs = genExpr(rhs)
@@ -1409,7 +1411,7 @@ class JSCodeGen()(implicit ctx: Context) {
14091411

14101412
/** Gen JS code for an array operation (get, set or length) */
14111413
private def genArrayOp(tree: Tree, code: Int): js.Tree = {
1412-
import scala.tools.nsc.backend.ScalaPrimitives._
1414+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
14131415

14141416
implicit val pos = tree.pos
14151417

@@ -1423,8 +1425,9 @@ class JSCodeGen()(implicit ctx: Context) {
14231425
case defn.ArrayOf(el) => el
14241426
case JavaArrayType(el) => el
14251427
case tpe =>
1426-
ctx.error(s"expected Array $tpe")
1427-
ErrorType
1428+
val msg = ex"expected Array $tpe"
1429+
ctx.error(msg)
1430+
ErrorType(msg)
14281431
}
14291432

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

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

14771480
implicit val pos = tree.pos
14781481

@@ -1881,7 +1884,7 @@ class JSCodeGen()(implicit ctx: Context) {
18811884
}.unzip
18821885

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

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

21802183
val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx =>
21812184
for ((name, tpe) <- paramNamesAndTypes)

compiler/sjs/backend/sjs/JSDefinitions.scala renamed to compiler/src/dotty/tools/backend/backend/sjs/JSDefinitions.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,15 @@ final class JSDefinitions()(implicit ctx: Context) {
183183
*
184184
* This is similar to `isVarArityClass` in `Definitions.scala`.
185185
*/
186-
private def isScalaJSVarArityClass(cls: Symbol, prefix: Name): Boolean = {
186+
private def isScalaJSVarArityClass(cls: Symbol, prefix: String): Boolean = {
187187
val name = scalajsClassName(cls)
188-
name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
188+
name.startsWith(prefix) && name.toString.drop(prefix.length).forall(_.isDigit)
189189
}
190190

191191
def isJSFunctionClass(cls: Symbol): Boolean =
192-
isScalaJSVarArityClass(cls, nme.Function)
193-
194-
private val ThisFunctionName = termName("ThisFunction")
192+
isScalaJSVarArityClass(cls, str.Function)
195193

196194
def isJSThisFunctionClass(cls: Symbol): Boolean =
197-
isScalaJSVarArityClass(cls, ThisFunctionName)
195+
isScalaJSVarArityClass(cls, "ThisFunction")
198196

199197
}

compiler/sjs/backend/sjs/JSEncoding.scala renamed to compiler/src/dotty/tools/backend/backend/sjs/JSEncoding.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ object JSEncoding {
209209
}
210210

211211
def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean =
212-
sym.name.isImplClassName
212+
sym.name.endsWith(nme.IMPL_CLASS_SUFFIX.toString)
213213

214214
def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = {
215215
if (sym == defn.ObjectClass) jstpe.AnyType

compiler/sjs/backend/sjs/JSInterop.scala renamed to compiler/src/dotty/tools/backend/backend/sjs/JSInterop.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Flags._
66
import Symbols._
77
import NameOps._
88
import StdNames._
9+
import NameKinds.DefaultGetterName
910

1011
import JSDefinitions._
1112

@@ -66,11 +67,17 @@ object JSInterop {
6667
* is a JS type.
6768
*/
6869
def isJSDefaultParam(sym: Symbol)(implicit ctx: Context): Boolean = {
69-
sym.name.isDefaultGetterName && {
70+
sym.name.is(DefaultGetterName) && {
7071
val owner = sym.owner
71-
if (owner.is(ModuleClass) &&
72-
sym.name.asTermName.defaultGetterToMethod == nme.CONSTRUCTOR) {
73-
isJSType(owner.linkedClass)
72+
if (owner.is(ModuleClass)) {
73+
val isConstructor = sym.name match {
74+
case DefaultGetterName(methName, _) => methName == nme.CONSTRUCTOR
75+
case _ => false
76+
}
77+
if (isConstructor)
78+
isJSType(owner.linkedClass)
79+
else
80+
isJSType(owner)
7481
} else {
7582
isJSType(owner)
7683
}

compiler/sjs/backend/sjs/JSPrimitives.scala renamed to compiler/src/dotty/tools/backend/backend/sjs/JSPrimitives.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ object JSPrimitives {
4343

4444
class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
4545
import JSPrimitives._
46-
import scala.tools.nsc.backend.ScalaPrimitives._
46+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
4747

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

compiler/sjs/backend/sjs/ScopedVar.scala renamed to compiler/src/dotty/tools/backend/backend/sjs/ScopedVar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import language.implicitConversions
55
class ScopedVar[A](init: A) {
66
import ScopedVar.Assignment
77

8-
private[this] var value = init
8+
private[ScopedVar] var value = init
99

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

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import util.FreshNameCreator
1515
import core.DenotTransformers.DenotTransformer
1616
import core.Denotations.SingleDenotation
1717

18+
import dotty.tools.backend.sjs
1819
import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls}
1920
import dotty.tools.dotc.transform.localopt.Simplify
2021

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

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import printing.XprintMode
2222
import scala.annotation.tailrec
2323
import dotty.tools.io.VirtualFile
2424
import scala.util.control.NonFatal
25+
import dotty.tools.backend.sjs
2526

2627
/** A compiler run. Exports various methods to compile source files */
2728
class Run(comp: Compiler, ictx: Context) {
@@ -38,7 +39,23 @@ class Run(comp: Compiler, ictx: Context) {
3839
*/
3940
protected[this] def rootContext(implicit ctx: Context): Context = {
4041
ctx.initialize()(ctx)
41-
ctx.setPhasePlan(comp.phases)
42+
43+
val actualPhases = if (ctx.settings.scalajs.value) {
44+
// Remove phases that Scala.js does not want
45+
comp.phases.mapConserve(_.filter {
46+
case _: transform.FunctionalInterfaces => false
47+
case _ => true
48+
}).filter(_.nonEmpty)
49+
} else {
50+
// Remove Scala.js-related phases
51+
comp.phases.mapConserve(_.filter {
52+
case _: sjs.GenSJSIR => false
53+
case _ => true
54+
}).filter(_.nonEmpty)
55+
}
56+
57+
ctx.setPhasePlan(actualPhases)
58+
4259
val rootScope = new MutableScope
4360
val bootstrap = ctx.fresh
4461
.setPeriod(Period(comp.nextRunId, FirstPhaseId))

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class ScalaSettings extends Settings.SettingGroup {
3333
val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/)
3434
val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
3535
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")
36+
val scalajs = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).")
3637
val unchecked = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
3738
val uniqid = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.")
3839
val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.")

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import reporting.diagnostic.Message
2727
import collection.mutable
2828
import collection.immutable.BitSet
2929
import printing._
30-
import config.{Settings, ScalaSettings, Platform, JavaPlatform}
30+
import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform}
3131
import language.implicitConversions
3232
import DenotTransformers.DenotTransformer
3333
import util.Property.Key
@@ -547,7 +547,8 @@ object Contexts {
547547
}
548548

549549
protected def newPlatform(implicit ctx: Context): Platform =
550-
new JavaPlatform
550+
if (settings.scalajs.value) new SJSPlatform
551+
else new JavaPlatform
551552

552553
/** The loader that loads the members of _root_ */
553554
def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root)

compiler/src/dotty/tools/dotc/transform/GetClass.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ class GetClass extends MiniPhase {
2020

2121
override def phaseName: String = "getClass"
2222

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

2625
override def transformApply(tree: Apply)(implicit ctx: Context): Tree = {
2726
import ast.Trees._

0 commit comments

Comments
 (0)