Skip to content

Commit 40a96c8

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 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 4385494 commit 40a96c8

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
@@ -7,6 +7,7 @@ import typer.{FrontEnd, RefChecks}
77
import Phases.Phase
88
import transform._
99
import dotty.tools.backend.jvm.{CollectSuperCalls, GenBCode}
10+
import dotty.tools.backend.sjs
1011
import dotty.tools.dotc.transform.localopt.StringInterpolatorOpt
1112

1213
/** The central class of the dotc compiler. The job of a compiler is to create
@@ -113,6 +114,7 @@ class Compiler {
113114

114115
/** Generate the output of the compilation */
115116
protected def backendPhases: List[List[Phase]] =
117+
List(new sjs.GenSJSIR) :: // Generate .sjsir files for Scala.js (not enabled by default)
116118
List(new GenBCode) :: // Generate JVM bytecode
117119
Nil
118120

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import collection.mutable
2727
import dotty.tools.io.VirtualFile
2828

2929
import scala.util.control.NonFatal
30+
import dotty.tools.backend.sjs
3031

3132
/** A compiler run. Exports various methods to compile source files */
3233
class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
@@ -41,7 +42,23 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
4142
*/
4243
protected[this] def rootContext(implicit ctx: Context): Context = {
4344
ctx.initialize()(ctx)
44-
ctx.base.setPhasePlan(comp.phases)
45+
46+
val actualPhases = if (ctx.settings.scalajs.value) {
47+
// Remove phases that Scala.js does not want
48+
comp.phases.mapConserve(_.filter {
49+
case _: transform.FunctionalInterfaces => false
50+
case _ => true
51+
}).filter(_.nonEmpty)
52+
} else {
53+
// Remove Scala.js-related phases
54+
comp.phases.mapConserve(_.filter {
55+
case _: sjs.GenSJSIR => false
56+
case _ => true
57+
}).filter(_.nonEmpty)
58+
}
59+
60+
ctx.base.setPhasePlan(actualPhases)
61+
4562
val rootScope = new MutableScope
4663
val bootstrap = ctx.fresh
4764
.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
@@ -34,6 +34,7 @@ class ScalaSettings extends Settings.SettingGroup {
3434
val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/)
3535
val target: Setting[String] = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
3636
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")
37+
val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).")
3738
val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
3839
val uniqid: Setting[Boolean] = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.")
3940
val usejavacp: Setting[Boolean] = 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
@@ -22,7 +22,7 @@ import reporting._
2222
import reporting.diagnostic.Message
2323
import collection.mutable
2424
import printing._
25-
import config.{JavaPlatform, Platform, ScalaSettings}
25+
import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings}
2626

2727
import scala.annotation.internal.sharable
2828

@@ -590,7 +590,8 @@ object Contexts {
590590
}
591591

592592
protected def newPlatform(implicit ctx: Context): Platform =
593-
new JavaPlatform
593+
if (settings.scalajs.value) new SJSPlatform
594+
else new JavaPlatform
594595

595596
/** The loader that loads the members of _root_ */
596597
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
@@ -20,7 +20,7 @@ class GetClass extends MiniPhase {
2020
override def phaseName: String = "getClass"
2121

2222
// getClass transformation should be applied to specialized methods
23-
override def runsAfter: Set[String] = Set(Erasure.name, FunctionalInterfaces.name)
23+
override def runsAfter: Set[String] = Set(Erasure.name)
2424

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

0 commit comments

Comments
 (0)