From 6e7ea68ffb66a314750e54b46f3d42fb4c52ffd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 29 Apr 2024 19:47:45 +0200 Subject: [PATCH 1/5] Fix scala-js/scala-js#4801: Rebase the super JS type as seen from the this type in JS super call. When doing a super call to a method of a path-dependent JS super class, the `superClass.typeRef` is only valid as seen from the super class' thisType. We need to rebase it with `asSeenFrom` to be in the context of the current class' thisType. Forward port of the upstream commit https://github.com/scala-js/scala-js/commit/3cef9d095172b2b5b8189684991d55fa16878875 --- .../dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala b/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala index 853fead6f799..5c7119860ae4 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala @@ -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 } From 34742325408fc155ee01000c76c26580b5b9bd2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 26 Apr 2024 15:51:28 +0200 Subject: [PATCH 2/5] Upgrade to Scala.js 1.14.0. --- project/Build.scala | 1 + project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index dcf6ec13760d..a9095e924c1b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1581,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 diff --git a/project/plugins.sbt b/project/plugins.sbt index d378848561b8..63ace3e44e26 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -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.14.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21") From 710aad8e35b59251b0114a12c7269055b0b9700a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 29 Apr 2024 19:55:36 +0200 Subject: [PATCH 3/5] Upgrade to Scala.js 1.15.0. --- compiler/test/dotty/Properties.scala | 3 +++ compiler/test/dotty/tools/vulpix/TestConfiguration.scala | 1 + project/Build.scala | 1 + project/plugins.sbt | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/test/dotty/Properties.scala b/compiler/test/dotty/Properties.scala index e085b0de4875..86e0788a3b8f 100644 --- a/compiler/test/dotty/Properties.scala +++ b/compiler/test/dotty/Properties.scala @@ -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") } diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 086d590fbfc7..e97ef47e6fef 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -52,6 +52,7 @@ object TestConfiguration { lazy val scalaJSClasspath = mkClasspath(List( Properties.scalaJSJavalib, + Properties.scalaJSScalalib, Properties.scalaJSLibrary, Properties.dottyLibraryJS )) diff --git a/project/Build.scala b/project/Build.scala index a9095e924c1b..f265fe1ff359 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1650,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"), ) }, diff --git a/project/plugins.sbt b/project/plugins.sbt index 63ace3e44e26..91591e8b104f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,7 +6,7 @@ libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.14.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.15.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21") From 78f3eeb15848e950619b91eeaf8e918f9a7d49aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 29 Apr 2024 22:09:42 +0200 Subject: [PATCH 4/5] Fix scala-js/scala-js#4929: Fix logic for moving early assignements in JS ctors. Previously, we moved all statements in the constructors after the super constructor call. However, it turns out that there are statements that must be kept before, notably local `val`s generated for default arguments to the super constructor. We now keep statements where they are by default. We only move statements of the form `C.this.field = ident;`, which are the only ones that require access to `this`. Forward port of the upstream commit https://github.com/scala-js/scala-js/commit/2e4594f0739cc58b74f1de7d5c4cc51b72a1371a --- .../dotty/tools/backend/sjs/JSCodeGen.scala | 68 +++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 8328afd52573..ca933c44f70e 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -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 = { From 8ebef0f33bf4d8bd749c7e3d95b9aedea0cfe4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 29 Apr 2024 20:01:31 +0200 Subject: [PATCH 5/5] Upgrade to Scala.js 1.16.0. Code changes are forward ports of the following refactorings: * Remove the parameters to StoreModule IR nodes. https://github.com/scala-js/scala-js/commit/659d51808b94e46a13efd7599d6119d23ea07dfc * Refactor: Make FieldName a composite of ClassName and SimpleFieldName. https://github.com/scala-js/scala-js/commit/723663b76a4dc2775dbab12f3c268e33990b6b55 --- .../src/dotty/tools/backend/sjs/JSCodeGen.scala | 16 ++++++---------- .../src/dotty/tools/backend/sjs/JSEncoding.scala | 4 ++-- project/Build.scala | 3 ++- project/plugins.sbt | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index ca933c44f70e..6e2449b5c299 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -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, @@ -2243,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 } @@ -4463,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 { @@ -4495,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) } diff --git a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index f2b90d5b1161..098f592daa30 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -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 @@ -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)) diff --git a/project/Build.scala b/project/Build.scala index f265fe1ff359..ebe46a2e85d1 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1508,7 +1508,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), diff --git a/project/plugins.sbt b/project/plugins.sbt index 91591e8b104f..59e58007a4a0 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,7 +6,7 @@ libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.15.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21")