Skip to content

Commit

Permalink
Upgrade to Scala.js 1.16.0. (#20294)
Browse files Browse the repository at this point in the history
The commit messages mention the upstream commits that were forward
ported.

Tests come for free in the "Upgrade to Scala.js 1.x.0." commits.
  • Loading branch information
sjrd committed May 6, 2024
2 parents 1a103c0 + 8ebef0f commit 827df03
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 34 deletions.
84 changes: 55 additions & 29 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ class JSCodeGen()(using genCtx: Context) {
val className = encodeClassName(classSym)
val body = js.Block(
js.LoadModule(className),
js.SelectStatic(className, fieldIdent)(irTpe))
js.SelectStatic(fieldIdent)(irTpe))
staticGetterDefs += js.MethodDef(
js.MemberFlags.empty.withNamespace(js.MemberNamespace.PublicStatic),
encodeStaticMemberSym(f), originalName, Nil, irTpe,
Expand Down Expand Up @@ -1146,42 +1146,72 @@ class JSCodeGen()(using genCtx: Context) {

private def genPrimaryJSClassCtor(dd: DefDef): PrimaryJSCtor = {
val sym = dd.symbol
val Block(stats, _) = dd.rhs: @unchecked
assert(sym.isPrimaryConstructor, s"called with non-primary ctor: $sym")

var preSuperStats = List.newBuilder[js.Tree]
var jsSuperCall: Option[js.JSSuperConstructorCall] = None
val jsStats = List.newBuilder[js.Tree]
val postSuperStats = List.newBuilder[js.Tree]

/* Move all statements after the super constructor call since JS
* cannot access `this` before the super constructor call.
/* Move param accessor initializers after the super constructor call since
* JS cannot access `this` before the super constructor call.
*
* dotc inserts statements before the super constructor call for param
* accessor initializers (including val's and var's declared in the
* params). We move those after the super constructor call, and are
* therefore executed later than for a Scala class.
* params). Those statements are assignments whose rhs'es are always simple
* Idents (the constructor params).
*
* There can also be local `val`s before the super constructor call for
* default arguments to the super constructor. These must remain before.
*
* Our strategy is therefore to move only the field assignments after the
* super constructor call. They are therefore executed later than for a
* Scala class (as specified for non-native JS classes semantics).
* However, side effects and evaluation order of all the other
* computations remains unchanged.
*/
withPerMethodBodyState(sym) {
stats.foreach {
case tree @ Apply(fun @ Select(Super(This(_), _), _), args)
if fun.symbol.isClassConstructor =>
assert(jsSuperCall.isEmpty, s"Found 2 JS Super calls at ${dd.sourcePos}")
implicit val pos: Position = tree.span
jsSuperCall = Some(js.JSSuperConstructorCall(genActualJSArgs(fun.symbol, args)))
def isThisField(tree: Tree): Boolean = tree match {
case Select(ths: This, _) => ths.symbol == currentClassSym.get
case tree: Ident => desugarIdent(tree).exists(isThisField(_))
case _ => false
}

case stat =>
val jsStat = genStat(stat)
assert(jsSuperCall.isDefined || !jsStat.isInstanceOf[js.VarDef],
"Trying to move a local VarDef after the super constructor call of a non-native JS class at " +
dd.sourcePos)
jsStats += jsStat
def rec(tree: Tree): Unit = {
tree match {
case Block(stats, expr) =>
stats.foreach(rec(_))
rec(expr)

case tree @ Apply(fun @ Select(Super(This(_), _), _), args)
if fun.symbol.isClassConstructor =>
assert(jsSuperCall.isEmpty, s"Found 2 JS Super calls at ${dd.sourcePos}")
implicit val pos: Position = tree.span
jsSuperCall = Some(js.JSSuperConstructorCall(genActualJSArgs(fun.symbol, args)))

case tree if jsSuperCall.isDefined =>
// Once we're past the super constructor call, everything goes after.
postSuperStats += genStat(tree)

case Assign(lhs, Ident(_)) if isThisField(lhs) =>
/* If that shape appears before the jsSuperCall, it is a param
* accessor initializer. We move it.
*/
postSuperStats += genStat(tree)

case stat =>
// Other statements are left before.
preSuperStats += genStat(stat)
}
}

rec(dd.rhs)
}

assert(jsSuperCall.isDefined,
s"Did not find Super call in primary JS construtor at ${dd.sourcePos}")

new PrimaryJSCtor(sym, genParamsAndInfo(sym, dd.paramss),
js.JSConstructorBody(Nil, jsSuperCall.get, jsStats.result())(dd.span))
js.JSConstructorBody(preSuperStats.result(), jsSuperCall.get, postSuperStats.result())(dd.span))
}

private def genSecondaryJSClassCtor(dd: DefDef): SplitSecondaryJSCtor = {
Expand Down Expand Up @@ -2213,10 +2243,7 @@ class JSCodeGen()(using genCtx: Context) {
if (isStaticModule(currentClassSym) && !isModuleInitialized.get.value &&
currentMethodSym.get.isClassConstructor) {
isModuleInitialized.get.value = true
val className = encodeClassName(currentClassSym)
val thisType = jstpe.ClassType(className)
val initModule = js.StoreModule(className, js.This()(thisType))
js.Block(superCall, initModule)
js.Block(superCall, js.StoreModule())
} else {
superCall
}
Expand Down Expand Up @@ -4433,13 +4460,12 @@ class JSCodeGen()(using genCtx: Context) {
js.JSSelect(qual, genPrivateFieldsSymbol()),
encodeFieldSymAsStringLiteral(sym))
} else {
js.JSPrivateSelect(qual, encodeClassName(sym.owner),
encodeFieldSym(sym))
js.JSPrivateSelect(qual, encodeFieldSym(sym))
}

(f, true)
} else if (sym.hasAnnotation(jsdefn.JSExportTopLevelAnnot)) {
val f = js.SelectStatic(encodeClassName(sym.owner), encodeFieldSym(sym))(jstpe.AnyType)
val f = js.SelectStatic(encodeFieldSym(sym))(jstpe.AnyType)
(f, true)
} else if (sym.hasAnnotation(jsdefn.JSExportStaticAnnot)) {
val jsName = sym.getAnnotation(jsdefn.JSExportStaticAnnot).get.argumentConstantString(0).getOrElse {
Expand All @@ -4465,9 +4491,9 @@ class JSCodeGen()(using genCtx: Context) {

val f =
if sym.is(JavaStatic) then
js.SelectStatic(className, fieldIdent)(irType)
js.SelectStatic(fieldIdent)(irType)
else
js.Select(qual, className, fieldIdent)(irType)
js.Select(qual, fieldIdent)(irType)

(f, boxed)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/backend/sjs/JSEncoding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import dotty.tools.dotc.transform.sjs.JSSymUtils.*

import org.scalajs.ir
import org.scalajs.ir.{Trees => js, Types => jstpe}
import org.scalajs.ir.Names.{LocalName, LabelName, FieldName, SimpleMethodName, MethodName, ClassName}
import org.scalajs.ir.Names.{LocalName, LabelName, SimpleFieldName, FieldName, SimpleMethodName, MethodName, ClassName}
import org.scalajs.ir.OriginalName
import org.scalajs.ir.OriginalName.NoOriginalName
import org.scalajs.ir.UTF8String
Expand Down Expand Up @@ -173,7 +173,7 @@ object JSEncoding {
}

def encodeFieldSym(sym: Symbol)(implicit ctx: Context, pos: ir.Position): js.FieldIdent =
js.FieldIdent(FieldName(encodeFieldSymAsString(sym)))
js.FieldIdent(FieldName(encodeClassName(sym.owner), SimpleFieldName(encodeFieldSymAsString(sym))))

def encodeFieldSymAsStringLiteral(sym: Symbol)(implicit ctx: Context, pos: ir.Position): js.StringLiteral =
js.StringLiteral(encodeFieldSymAsString(sym))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,11 @@ class ExplicitJSClasses extends MiniPhase with InfoTransformer { thisPhase =>
private def maybeWrapSuperCallWithContextualJSClassValue(tree: Tree)(using Context): Tree = {
methPart(tree) match {
case Select(sup: Super, _) if isInnerOrLocalJSClass(sup.symbol.asClass.superClass) =>
wrapWithContextualJSClassValue(sup.symbol.asClass.superClass.typeRef)(tree)
val superClass = sup.symbol.asClass.superClass
val jsClassTypeInSuperClass = superClass.typeRef
// scala-js#4801 Rebase the super class type on the current class' this type
val jsClassTypeAsSeenFromThis = jsClassTypeInSuperClass.asSeenFrom(currentClass.thisType, superClass)
wrapWithContextualJSClassValue(jsClassTypeAsSeenFromThis)(tree)
case _ =>
tree
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/test/dotty/Properties.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ object Properties {
/** scalajs-javalib jar */
def scalaJSJavalib: String = sys.props("dotty.tests.classes.scalaJSJavalib")

/** scalajs-scalalib jar */
def scalaJSScalalib: String = sys.props("dotty.tests.classes.scalaJSScalalib")

/** scalajs-library jar */
def scalaJSLibrary: String = sys.props("dotty.tests.classes.scalaJSLibrary")
}
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/vulpix/TestConfiguration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ object TestConfiguration {

lazy val scalaJSClasspath = mkClasspath(List(
Properties.scalaJSJavalib,
Properties.scalaJSScalalib,
Properties.scalaJSLibrary,
Properties.dottyLibraryJS
))
Expand Down
5 changes: 4 additions & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1507,7 +1507,8 @@ object Build {
"isNoModule" -> (moduleKind == ModuleKind.NoModule),
"isESModule" -> (moduleKind == ModuleKind.ESModule),
"isCommonJSModule" -> (moduleKind == ModuleKind.CommonJSModule),
"isFullOpt" -> (stage == FullOptStage),
"usesClosureCompiler" -> linkerConfig.closureCompiler,
"hasMinifiedNames" -> (linkerConfig.closureCompiler || linkerConfig.minify),
"compliantAsInstanceOfs" -> (sems.asInstanceOfs == CheckedBehavior.Compliant),
"compliantArrayIndexOutOfBounds" -> (sems.arrayIndexOutOfBounds == CheckedBehavior.Compliant),
"compliantArrayStores" -> (sems.arrayStores == CheckedBehavior.Compliant),
Expand Down Expand Up @@ -1580,6 +1581,7 @@ object Build {
-- "ReflectiveCallTest.scala" // uses many forms of structural calls that are not allowed in Scala 3 anymore
-- "UTF16Test.scala" // refutable pattern match
-- "CharsetTest.scala" // bogus @tailrec that Scala 2 ignores but Scala 3 flags as an error
-- "ClassDiffersOnlyInCaseTest.scala" // looks like the Scala 3 compiler itself does not deal with that
)).get

++ (dir / "shared/src/test/require-sam" ** "*.scala").get
Expand Down Expand Up @@ -1648,6 +1650,7 @@ object Build {
Seq(
"-Ddotty.tests.classes.dottyLibraryJS=" + dottyLibraryJSJar,
"-Ddotty.tests.classes.scalaJSJavalib=" + findArtifactPath(externalJSDeps, "scalajs-javalib"),
"-Ddotty.tests.classes.scalaJSScalalib=" + findArtifactPath(externalJSDeps, "scalajs-scalalib_2.13"),
"-Ddotty.tests.classes.scalaJSLibrary=" + findArtifactPath(externalJSDeps, "scalajs-library_2.13"),
)
},
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
libraryDependencySchemes +=
"org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")

addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21")

Expand Down

0 comments on commit 827df03

Please sign in to comment.