Skip to content

Commit 256175e

Browse files
smartersjrd
authored andcommitted
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 #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 #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 fcd51f5 commit 256175e

File tree

17 files changed

+151
-115
lines changed

17 files changed

+151
-115
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)
@@ -1006,7 +1008,7 @@ class JSCodeGen()(implicit ctx: Context) {
10061008

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

10111013
implicit val pos = tree.pos
10121014

@@ -1046,7 +1048,7 @@ class JSCodeGen()(implicit ctx: Context) {
10461048

10471049
/** Gen JS code for a simple unary operation. */
10481050
private def genSimpleUnaryOp(tree: Apply, arg: Tree, code: Int): js.Tree = {
1049-
import scala.tools.nsc.backend.ScalaPrimitives._
1051+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
10501052

10511053
implicit val pos = tree.pos
10521054

@@ -1087,7 +1089,7 @@ class JSCodeGen()(implicit ctx: Context) {
10871089

10881090
/** Gen JS code for a simple binary operation. */
10891091
private def genSimpleBinaryOp(tree: Apply, lhs: Tree, rhs: Tree, code: Int): js.Tree = {
1090-
import scala.tools.nsc.backend.ScalaPrimitives._
1092+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
10911093
import js.UnaryOp._
10921094

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

1268-
import scala.tools.nsc.backend.ScalaPrimitives._
1270+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
12691271

12701272
val genLhs = genExpr(lhs)
12711273
val genRhs = genExpr(rhs)
@@ -1394,7 +1396,7 @@ class JSCodeGen()(implicit ctx: Context) {
13941396

13951397
/** Gen JS code for an array operation (get, set or length) */
13961398
private def genArrayOp(tree: Tree, code: Int): js.Tree = {
1397-
import scala.tools.nsc.backend.ScalaPrimitives._
1399+
import scala.tools.nsc.backend.ScalaPrimitivesOps._
13981400

13991401
implicit val pos = tree.pos
14001402

@@ -1408,8 +1410,9 @@ class JSCodeGen()(implicit ctx: Context) {
14081410
case defn.ArrayOf(el) => el
14091411
case JavaArrayType(el) => el
14101412
case tpe =>
1411-
ctx.error(s"expected Array $tpe")
1412-
ErrorType
1413+
val msg = ex"expected Array $tpe"
1414+
ctx.error(msg)
1415+
ErrorType(msg)
14131416
}
14141417

14151418
def genSelect(): js.Tree =
@@ -1457,7 +1460,7 @@ class JSCodeGen()(implicit ctx: Context) {
14571460

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

14621465
implicit val pos = tree.pos
14631466

@@ -1866,7 +1869,7 @@ class JSCodeGen()(implicit ctx: Context) {
18661869
}.unzip
18671870

18681871
val formalParamNames = sym.info.paramNamess.flatten.drop(envSize)
1869-
val formalParamTypes = sym.info.paramTypess.flatten.drop(envSize)
1872+
val formalParamTypes = sym.info.paramInfoss.flatten.drop(envSize)
18701873
val (formalParams, actualParams) = formalParamNames.zip(formalParamTypes).map {
18711874
case (name, tpe) =>
18721875
val formalParam = js.ParamDef(freshLocalIdent(name.toString),
@@ -2160,7 +2163,7 @@ class JSCodeGen()(implicit ctx: Context) {
21602163
implicit pos: Position): List[js.Tree] = {
21612164

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

21652168
val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx =>
21662169
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
import dotty.tools.backend.jvm.{CollectSuperCalls, GenBCode}
18+
import dotty.tools.backend.sjs
1819
import dotty.tools.dotc.transform.localopt.StringInterpolatorOpt
1920

2021
/** The central class of the dotc compiler. The job of a compiler is to create
@@ -122,6 +123,7 @@ class Compiler {
122123

123124
/** Generate the output of the compilation */
124125
protected def backendPhases: List[List[Phase]] =
126+
List(new sjs.GenSJSIR) :: // Generate .sjsir files for Scala.js (not enabled by default)
125127
List(new GenBCode) :: // Generate JVM bytecode
126128
Nil
127129

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import scala.annotation.tailrec
3030
import dotty.tools.io.VirtualFile
3131

3232
import scala.util.control.NonFatal
33+
import dotty.tools.backend.sjs
3334

3435
/** A compiler run. Exports various methods to compile source files */
3536
class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
@@ -44,7 +45,23 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
4445
*/
4546
protected[this] def rootContext(implicit ctx: Context): Context = {
4647
ctx.initialize()(ctx)
47-
ctx.base.setPhasePlan(comp.phases)
48+
49+
val actualPhases = if (ctx.settings.scalajs.value) {
50+
// Remove phases that Scala.js does not want
51+
comp.phases.mapConserve(_.filter {
52+
case _: transform.FunctionalInterfaces => false
53+
case _ => true
54+
}).filter(_.nonEmpty)
55+
} else {
56+
// Remove Scala.js-related phases
57+
comp.phases.mapConserve(_.filter {
58+
case _: sjs.GenSJSIR => false
59+
case _ => true
60+
}).filter(_.nonEmpty)
61+
}
62+
63+
ctx.base.setPhasePlan(actualPhases)
64+
4865
val rootScope = new MutableScope
4966
val bootstrap = ctx.fresh
5067
.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
@@ -35,6 +35,7 @@ class ScalaSettings extends Settings.SettingGroup {
3535
val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/)
3636
val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
3737
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")
38+
val scalajs = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).")
3839
val unchecked = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
3940
val uniqid = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.")
4041
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.{JavaPlatform, Platform, ScalaSettings, Settings}
30+
import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings, Settings}
3131

3232
import scala.annotation.internal.sharable
3333

@@ -596,7 +596,8 @@ object Contexts {
596596
}
597597

598598
protected def newPlatform(implicit ctx: Context): Platform =
599-
new JavaPlatform
599+
if (settings.scalajs.value) new SJSPlatform
600+
else new JavaPlatform
600601

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class GetClass extends MiniPhase {
2121
override def phaseName: String = "getClass"
2222

2323
// getClass transformation should be applied to specialized methods
24-
override def runsAfter = Set(Erasure.name, FunctionalInterfaces.name)
24+
override def runsAfter = Set(Erasure.name)
2525

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

0 commit comments

Comments
 (0)