diff --git a/community-build/community-projects/scalatest b/community-build/community-projects/scalatest index d6eeedbfc1e0..ab674686d089 160000 --- a/community-build/community-projects/scalatest +++ b/community-build/community-projects/scalatest @@ -1 +1 @@ -Subproject commit d6eeedbfc1e04f2eff55506f07f93f448cc21407 +Subproject commit ab674686d089f13da2e29c3b78fe6c3ab0211189 diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index be0a25e210b1..8ffc9637a001 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -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 @@ -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 @@ -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 } @@ -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, @@ -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) @@ -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), @@ -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) @@ -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, @@ -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 @@ -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) } @@ -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}") @@ -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) @@ -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) @@ -3559,10 +3565,11 @@ 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, @@ -3570,23 +3577,32 @@ class JSCodeGen()(using genCtx: Context) { 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) } } } @@ -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) */ @@ -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) @@ -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 @@ -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") diff --git a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index b3d28b73d81c..959a05fd6c43 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -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 @@ -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) } @@ -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) @@ -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}") } } @@ -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 { diff --git a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala index 42205f9b70c2..425710c6be9a 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala @@ -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 @@ -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) } } @@ -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) diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala index 5aa35a277cb5..43b29a224564 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala @@ -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 { diff --git a/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala b/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala index 87208573eff9..aa08afdce323 100644 --- a/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala +++ b/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala @@ -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 diff --git a/project/Build.scala b/project/Build.scala index 18d54bfbf025..678ada479a33 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -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, diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 964acf50089f..a7f857e8a719 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -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 @@ -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"), diff --git a/project/plugins.sbt b/project/plugins.sbt index cc2ce6da6794..b61ec009db45 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.18.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21")