Skip to content

Upgrade to Scala.js 1.19.0. #23026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 49 additions & 32 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import dotty.tools.dotc.util.SourcePosition
import dotty.tools.dotc.report

import dotty.tools.sjs.ir
import dotty.tools.sjs.ir.{ClassKind, Position, Names => jsNames, Trees => js, Types => jstpe}
import dotty.tools.sjs.ir.{ClassKind, Position, Trees => js, Types => jstpe, WellKnownNames => jswkn}
import dotty.tools.sjs.ir.Names.{ClassName, LocalName, MethodName, SimpleMethodName}
import dotty.tools.sjs.ir.OriginalName
import dotty.tools.sjs.ir.OriginalName.NoOriginalName
Expand Down Expand Up @@ -133,7 +133,7 @@ class JSCodeGen()(using genCtx: Context) {
def currentThisType: jstpe.Type = {
currentThisTypeNullable match {
case tpe @ jstpe.ClassType(cls, _) =>
jstpe.BoxedClassToPrimType.getOrElse(cls, tpe.toNonNullable)
jswkn.BoxedClassToPrimType.getOrElse(cls, tpe.toNonNullable)
case tpe @ jstpe.AnyType =>
// We are in a JS class, in which even `this` is nullable
tpe
Expand Down Expand Up @@ -424,7 +424,7 @@ class JSCodeGen()(using genCtx: Context) {

val staticInitializerStats = reflectInit ::: staticModuleInit
if (staticInitializerStats.nonEmpty)
List(genStaticConstructorWithStats(ir.Names.StaticInitializerName, js.Block(staticInitializerStats)))
List(genStaticConstructorWithStats(jswkn.StaticInitializerName, js.Block(staticInitializerStats)))
else
Nil
}
Expand Down Expand Up @@ -453,7 +453,7 @@ class JSCodeGen()(using genCtx: Context) {
originalName,
ClassKind.Class,
None,
Some(js.ClassIdent(ir.Names.ObjectClass)),
Some(js.ClassIdent(jswkn.ObjectClass)),
Nil,
None,
None,
Expand Down Expand Up @@ -574,7 +574,7 @@ class JSCodeGen()(using genCtx: Context) {

if (staticFields.nonEmpty) {
generatedMethods +=
genStaticConstructorWithStats(ir.Names.ClassInitializerName, genLoadModule(companionModuleClass))
genStaticConstructorWithStats(jswkn.ClassInitializerName, genLoadModule(companionModuleClass))
}

(staticFields, staticExports)
Expand Down Expand Up @@ -1000,7 +1000,7 @@ class JSCodeGen()(using genCtx: Context) {
val fqcnArg = js.StringLiteral(sym.fullName.toString)
val runtimeClassArg = js.ClassOf(toTypeRef(sym.info))
val loadModuleFunArg =
js.Closure(arrow = true, Nil, Nil, None, genLoadModule(sym), Nil)
js.Closure(js.ClosureFlags.arrow, Nil, Nil, None, jstpe.AnyType, genLoadModule(sym), Nil)

val stat = genApplyMethod(
genLoadModule(jsdefn.ReflectModule),
Expand Down Expand Up @@ -1035,7 +1035,7 @@ class JSCodeGen()(using genCtx: Context) {

val paramTypesArray = js.JSArrayConstr(parameterTypes)

val newInstanceFun = js.Closure(arrow = true, Nil, formalParams, None, {
val newInstanceFun = js.Closure(js.ClosureFlags.arrow, Nil, formalParams, None, jstpe.AnyType, {
js.New(encodeClassName(sym), encodeMethodSym(ctor), actualParams)
}, Nil)

Expand Down Expand Up @@ -2389,7 +2389,7 @@ class JSCodeGen()(using genCtx: Context) {
// Make new class def with static members
val newClassDef = {
implicit val pos = originalClassDef.pos
val parent = js.ClassIdent(jsNames.ObjectClass)
val parent = js.ClassIdent(jswkn.ObjectClass)
js.ClassDef(originalClassDef.name, originalClassDef.originalName,
ClassKind.AbstractJSType, None, Some(parent), interfaces = Nil,
jsSuperClass = None, jsNativeLoadSpec = None, fields = Nil,
Expand Down Expand Up @@ -2427,7 +2427,7 @@ class JSCodeGen()(using genCtx: Context) {
js.VarRef(selfIdent.name)(jstpe.AnyType)

def memberLambda(params: List[js.ParamDef], restParam: Option[js.ParamDef], body: js.Tree)(implicit pos: ir.Position): js.Closure =
js.Closure(arrow = false, captureParams = Nil, params, restParam, body, captureValues = Nil)
js.Closure(js.ClosureFlags.function, captureParams = Nil, params, restParam, jstpe.AnyType, body, captureValues = Nil)

val fieldDefinitions = jsFieldDefs.toList.map { fdef =>
implicit val pos = fdef.pos
Expand Down Expand Up @@ -2539,7 +2539,8 @@ class JSCodeGen()(using genCtx: Context) {
beforeSuper ::: superCall ::: afterSuper
}

val closure = js.Closure(arrow = true, jsClassCaptures, Nil, None,
// Wrap everything in a lambda, for namespacing
val closure = js.Closure(js.ClosureFlags.arrow, jsClassCaptures, Nil, None, jstpe.AnyType,
js.Block(inlinedCtorStats, selfRef), jsSuperClassValue :: args)
js.JSFunctionApply(closure, Nil)
}
Expand Down Expand Up @@ -3350,7 +3351,7 @@ class JSCodeGen()(using genCtx: Context) {

// Sanity check: we can handle Ints and Strings (including `null`s), but nothing else
genSelector.tpe match {
case jstpe.IntType | jstpe.ClassType(jsNames.BoxedStringClass, _) | jstpe.NullType | jstpe.NothingType =>
case jstpe.IntType | jstpe.ClassType(jswkn.BoxedStringClass, _) | jstpe.NullType | jstpe.NothingType =>
// ok
case _ =>
abortMatch(s"Invalid selector type ${genSelector.tpe}")
Expand Down Expand Up @@ -3514,6 +3515,8 @@ class JSCodeGen()(using genCtx: Context) {
atPhase(elimRepeatedPhase)(samMethod.info.paramInfoss.flatten.exists(_.isRepeatedParam))
}
}
val isFunctionXXL =
funInterfaceSym.name == tpnme.FunctionXXL && funInterfaceSym.owner == defn.ScalaRuntimePackageClass

val formalParamNames = sym.info.paramNamess.flatten.drop(envSize)
val formalParamTypes = sym.info.paramInfoss.flatten.drop(envSize)
Expand All @@ -3523,8 +3526,11 @@ class JSCodeGen()(using genCtx: Context) {

val formalAndActualParams = formalParamNames.lazyZip(formalParamTypes).lazyZip(formalParamRepeateds).map {
(name, tpe, repeated) =>
val formalTpe =
if (isFunctionXXL) jstpe.ArrayType(ObjectArrayTypeRef, nullable = true)
else jstpe.AnyType
val formalParam = js.ParamDef(freshLocalIdent(name),
OriginalName(name.toString), jstpe.AnyType, mutable = false)
OriginalName(name.toString), formalTpe, mutable = false)
val actualParam =
if (repeated) genJSArrayToVarArgs(formalParam.ref)(tree.sourcePos)
else unbox(formalParam.ref, tpe)
Expand Down Expand Up @@ -3559,34 +3565,44 @@ class JSCodeGen()(using genCtx: Context) {
if (isThisFunction) {
val thisParam :: otherParams = formalParams: @unchecked
js.Closure(
arrow = false,
js.ClosureFlags.function,
formalCaptures,
otherParams,
restParam,
jstpe.AnyType,
js.Block(
js.VarDef(thisParam.name, thisParam.originalName,
thisParam.ptpe, mutable = false,
js.This()(thisParam.ptpe)(thisParam.pos))(thisParam.pos),
genBody),
actualCaptures)
} else {
val closure = js.Closure(arrow = true, formalCaptures, formalParams, restParam, genBody, actualCaptures)
val closure = js.Closure(js.ClosureFlags.typed, formalCaptures,
formalParams, restParam, jstpe.AnyType, genBody, actualCaptures)

if (!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym)) {
val formalCount = formalParams.size
val cls = ClassName("scala.scalajs.runtime.AnonFunction" + formalCount)
val ctorName = MethodName.constructor(
jstpe.ClassRef(ClassName("scala.scalajs.js.Function" + formalCount)) :: Nil)
js.New(cls, js.MethodIdent(ctorName), List(closure))
} else if (funInterfaceSym.name == tpnme.FunctionXXL && funInterfaceSym.owner == defn.ScalaRuntimePackageClass) {
val cls = ClassName("scala.scalajs.runtime.AnonFunctionXXL")
val ctorName = MethodName.constructor(
jstpe.ClassRef(ClassName("scala.scalajs.js.Function1")) :: Nil)
js.New(cls, js.MethodIdent(ctorName), List(closure))
val descriptor = js.NewLambda.Descriptor(
superClass = encodeClassName(defn.AbstractFunctionClass(formalCount)),
interfaces = Nil,
methodName = MethodName(applySimpleMethodName, List.fill(formalCount)(jswkn.ObjectRef), jswkn.ObjectRef),
paramTypes = List.fill(formalCount)(jstpe.AnyType),
resultType = jstpe.AnyType
)
js.NewLambda(descriptor, closure)(encodeClassType(defn.FunctionSymbol(formalCount)).toNonNullable)
} else if (isFunctionXXL) {
val descriptor = js.NewLambda.Descriptor(
superClass = jswkn.ObjectClass,
interfaces = List(encodeClassName(defn.FunctionXXLClass)),
methodName = MethodName(applySimpleMethodName, List(ObjectArrayTypeRef), jswkn.ObjectRef),
paramTypes = List(jstpe.ArrayType(ObjectArrayTypeRef, nullable = true)),
resultType = jstpe.AnyType
)
js.NewLambda(descriptor, closure)(encodeClassType(funInterfaceSym).toNonNullable)
} else {
assert(funInterfaceSym.isJSType,
s"Invalid functional interface $funInterfaceSym reached the back-end")
closure
closure.copy(flags = js.ClosureFlags.arrow)
}
}
}
Expand Down Expand Up @@ -3699,8 +3715,8 @@ class JSCodeGen()(using genCtx: Context) {
}

private def genThrowClassCastException()(implicit pos: Position): js.Tree = {
js.UnaryOp(js.UnaryOp.Throw, js.New(jsNames.ClassCastExceptionClass,
js.MethodIdent(jsNames.NoArgConstructorName), Nil))
js.UnaryOp(js.UnaryOp.Throw, js.New(jswkn.ClassCastExceptionClass,
js.MethodIdent(jswkn.NoArgConstructorName), Nil))
}

/** Gen JS code for an isInstanceOf test (for reference types only) */
Expand Down Expand Up @@ -3987,7 +4003,7 @@ class JSCodeGen()(using genCtx: Context) {
case arg: js.JSGlobalRef => js.JSTypeOfGlobalRef(arg)
case _ => js.JSUnaryOp(js.JSUnaryOp.typeof, arg)
}
js.AsInstanceOf(typeofExpr, jstpe.ClassType(jsNames.BoxedStringClass, nullable = true))
js.AsInstanceOf(typeofExpr, jstpe.ClassType(jswkn.BoxedStringClass, nullable = true))

case STRICT_EQ =>
// js.special.strictEquals(arg1, arg2)
Expand Down Expand Up @@ -4235,7 +4251,7 @@ class JSCodeGen()(using genCtx: Context) {
"literal classOf[T] expressions (typically compiler-generated). " +
"Other uses are not supported in Scala.js.",
otherTree.sourcePos)
(jstpe.AnyType, jstpe.ClassRef(jsNames.ObjectClass))
(jstpe.AnyType, jstpe.ClassRef(jswkn.ObjectClass))
}

// Gen the actual args, downcasting them to the formal param types
Expand Down Expand Up @@ -4870,16 +4886,17 @@ object JSCodeGen {
private val JSObjectClassName = ClassName("scala.scalajs.js.Object")
private val JavaScriptExceptionClassName = ClassName("scala.scalajs.js.JavaScriptException")

private val ObjectClassRef = jstpe.ClassRef(ir.Names.ObjectClass)
private val ObjectArrayTypeRef = jstpe.ArrayTypeRef(jswkn.ObjectRef, 1)

private val applySimpleMethodName = SimpleMethodName("apply")
private val newSimpleMethodName = SimpleMethodName("new")

private val selectedValueMethodName = MethodName("selectedValue", Nil, ObjectClassRef)
private val selectedValueMethodName = MethodName("selectedValue", Nil, jswkn.ObjectRef)

private val JLRArrayNewInstanceMethodName =
MethodName("newInstance", List(jstpe.ClassRef(jsNames.ClassClass), jstpe.ArrayTypeRef(jstpe.IntRef, 1)), ObjectClassRef)
MethodName("newInstance", List(jstpe.ClassRef(jswkn.ClassClass), jstpe.ArrayTypeRef(jstpe.IntRef, 1)), jswkn.ObjectRef)

private val ObjectArgConstructorName = MethodName.constructor(List(ObjectClassRef))
private val ObjectArgConstructorName = MethodName.constructor(List(jswkn.ObjectRef))

private val thisOriginalName = OriginalName("this")

Expand Down
11 changes: 7 additions & 4 deletions compiler/src/dotty/tools/backend/sjs/JSEncoding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import StdNames.*
import dotty.tools.dotc.transform.sjs.JSSymUtils.*

import dotty.tools.sjs.ir
import dotty.tools.sjs.ir.{Trees => js, Types => jstpe}
import dotty.tools.sjs.ir.{Trees => js, Types => jstpe, WellKnownNames => jswkn}
import dotty.tools.sjs.ir.Names.{LocalName, LabelName, SimpleFieldName, FieldName, SimpleMethodName, MethodName, ClassName}
import dotty.tools.sjs.ir.OriginalName
import dotty.tools.sjs.ir.OriginalName.NoOriginalName
Expand Down Expand Up @@ -235,7 +235,7 @@ object JSEncoding {

def encodeDynamicImportForwarderIdent(params: List[Symbol])(using Context, ir.Position): js.MethodIdent = {
val paramTypeRefs = params.map(sym => paramOrResultTypeRef(sym.info))
val resultTypeRef = jstpe.ClassRef(ir.Names.ObjectClass)
val resultTypeRef = jstpe.ClassRef(jswkn.ObjectClass)
val methodName = MethodName(dynamicImportForwarderSimpleName, paramTypeRefs, resultTypeRef)
js.MethodIdent(methodName)
}
Expand Down Expand Up @@ -282,7 +282,7 @@ object JSEncoding {
* - scala.Null to scala.runtime.Null$.
*/
if (sym1 == defn.BoxedUnitClass)
ir.Names.BoxedUnitClass
jswkn.BoxedUnitClass
else if (sym1 == defn.NothingClass)
ScalaRuntimeNothingClassName
else if (sym1 == defn.NullClass)
Expand Down Expand Up @@ -326,6 +326,9 @@ object JSEncoding {

case typeRef: jstpe.ArrayTypeRef =>
jstpe.ArrayType(typeRef, nullable = true)

case typeRef: jstpe.TransientTypeRef =>
throw AssertionError(s"Unexpected transient type ref $typeRef for ${typeRefInternal._2}")
}
}

Expand Down Expand Up @@ -359,7 +362,7 @@ object JSEncoding {
*/
def nonClassTypeRefToTypeRef(sym: Symbol): (jstpe.TypeRef, Symbol) = {
//assert(sym.isType && isCompilingArray, sym)
(jstpe.ClassRef(ir.Names.ObjectClass), defn.ObjectClass)
(jstpe.ClassRef(jswkn.ObjectClass), defn.ObjectClass)
}

tp.widenDealias match {
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import TypeErasure.ErasedValueType
import dotty.tools.dotc.util.{SourcePosition, SrcPos}
import dotty.tools.dotc.report

import dotty.tools.sjs.ir.{Position, Names => jsNames, Trees => js, Types => jstpe}
import dotty.tools.sjs.ir.Names.DefaultModuleID
import dotty.tools.sjs.ir.{Position, Names => jsNames, Trees => js, Types => jstpe, WellKnownNames => jswkn}
import dotty.tools.sjs.ir.OriginalName.NoOriginalName
import dotty.tools.sjs.ir.Position.NoPosition
import dotty.tools.sjs.ir.Trees.OptimizerHints
Expand Down Expand Up @@ -87,7 +86,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
symForAnnot.annotations.collect {
case annot if annot.symbol == jsdefn.JSExportTopLevelAnnot =>
val jsName = annot.argumentConstantString(0).get
val moduleID = annot.argumentConstantString(1).getOrElse(DefaultModuleID)
val moduleID = annot.argumentConstantString(1).getOrElse(jswkn.DefaultModuleID)
TopLevelExportInfo(moduleID, jsName)(annot.tree.sourcePos)
}
}
Expand Down Expand Up @@ -947,8 +946,8 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
case jstpe.FloatType => PrimitiveTypeTest(jstpe.FloatType, 7)
case jstpe.DoubleType => PrimitiveTypeTest(jstpe.DoubleType, 8)

case jstpe.ClassType(Names.BoxedUnitClass, _) => PrimitiveTypeTest(jstpe.UndefType, 0)
case jstpe.ClassType(Names.BoxedStringClass, _) => PrimitiveTypeTest(jstpe.StringType, 9)
case jstpe.ClassType(jswkn.BoxedUnitClass, _) => PrimitiveTypeTest(jstpe.UndefType, 0)
case jstpe.ClassType(jswkn.BoxedStringClass, _) => PrimitiveTypeTest(jstpe.StringType, 9)
case jstpe.ClassType(_, _) => InstanceOfTypeTest(tpe)

case jstpe.ArrayType(_, _) => InstanceOfTypeTest(tpe)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import dotty.tools.backend.sjs.JSDefinitions.jsdefn
import JSExportUtils.*
import JSSymUtils.*

import dotty.tools.sjs.ir.Names.DefaultModuleID
import dotty.tools.sjs.ir.WellKnownNames.DefaultModuleID
import dotty.tools.sjs.ir.Trees.TopLevelExportDef.isValidTopLevelExportName

object PrepJSExports {
Expand Down
29 changes: 23 additions & 6 deletions library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
package scala.scalajs.runtime

import scala.scalajs.js

@inline
final class AnonFunctionXXL(f: js.Function1[IArray[Object], Object]) extends scala.runtime.FunctionXXL {
override def apply(xs: IArray[Object]): Object = f(xs)
}
/* Before Scala.js 1.19, this class was concrete. It had a 1-argument
* constructor taking a js.Function[Array[Object], Object], and its `apply()`
* method called that function. This was similar to the `AnonFunctionN` classes
* of the Scala.js library (shared between Scala 2 and 3).
*
* In Scala.js 1.19, we introduced `NewLambda` nodes, which superseded these
* specialized classes with a compilation mode that is more efficient on Wasm.
* However, libraries compiled with earlier versions still contain references
* to `AnonFunctionXXL`.
*
* The IR deserializer patches allocations of the form
* New(AnonFunctionXXL, ctor, closure :: Nil)
* into
* NewLambda(AnonFunctionXXL, ..., (xs: Array[Object]) => closure(xs))
*
* When the `closure` is directly a JS `Closure` with the right signature
* (which is supposed to be always, as far as our codegens were concerned),
* it rewrites that as
* NewLambda(AnonFunctionXXL, ..., (closureParam: Array[Object]) => closureBody)
* which provides the best performance for old code.
*/
@deprecated("used by the codegen before Scala.js 1.19", since = "3.7.0")
sealed abstract class AnonFunctionXXL extends scala.runtime.FunctionXXL
1 change: 0 additions & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,6 @@ object Build {
"compliantNullPointers" -> (sems.nullPointers == CheckedBehavior.Compliant),
"compliantStringIndexOutOfBounds" -> (sems.stringIndexOutOfBounds == CheckedBehavior.Compliant),
"compliantModuleInit" -> (sems.moduleInit == CheckedBehavior.Compliant),
"strictFloats" -> sems.strictFloats,
"productionMode" -> sems.productionMode,
"esVersion" -> linkerConfig.esFeatures.esVersion.edition,
"useECMAScript2015Semantics" -> linkerConfig.esFeatures.useECMAScript2015Semantics,
Expand Down
11 changes: 11 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ object MiMaFilters {
// Additions that require a new minor version of the library
Build.mimaPreviousDottyVersion -> Seq(
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.readOnlyCapability"),

// Scala.js-only class
ProblemFilters.exclude[FinalClassProblem]("scala.scalajs.runtime.AnonFunctionXXL"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunctionXXL.this"),
),

// Additions since last LTS
Expand Down Expand Up @@ -97,6 +101,13 @@ object MiMaFilters {
// Breaking changes since last reference version
Build.mimaPreviousDottyVersion -> // Seq.empty, // We should never break backwards compatibility
Seq(
// Scala.js-only class, which is subject to IR deserializatiation hacks to preserve bincompat.
// It's OK. Scala.js did the same:
// https://github.com/scala-js/scala-js/blob/v1.19.0/project/BinaryIncompatibilities.scala#L66-L71
ProblemFilters.exclude[AbstractClassProblem]("scala.scalajs.runtime.AnonFunctionXXL"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunctionXXL.this"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunctionXXL.apply"),

// `ReversedMissingMethodProblem`s are acceptable. See comment in `Breaking changes since last LTS`.
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.FlexibleType"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.FlexibleTypeTypeTest"),
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.18.1")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0")

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

Expand Down
Loading