From cb71886c5bf5534fdc2f232f6c7bd552561d8c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 10 Apr 2023 10:11:03 +0200 Subject: [PATCH 01/27] Towards 1.13.2. --- ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala | 2 +- project/Build.scala | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala index 42c0038528..438d171d6f 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala @@ -17,7 +17,7 @@ import java.util.concurrent.ConcurrentHashMap import scala.util.matching.Regex object ScalaJSVersions extends VersionChecks( - current = "1.13.1", + current = "1.13.2-SNAPSHOT", binaryEmitted = "1.13" ) diff --git a/project/Build.scala b/project/Build.scala index 28b6f4d382..a272ecc0a7 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -254,7 +254,8 @@ object Build { val previousVersions = List("1.0.0", "1.0.1", "1.1.0", "1.1.1", "1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0", "1.5.1", "1.6.0", "1.7.0", "1.7.1", - "1.8.0", "1.9.0", "1.10.0", "1.10.1", "1.11.0", "1.12.0", "1.13.0") + "1.8.0", "1.9.0", "1.10.0", "1.10.1", "1.11.0", "1.12.0", "1.13.0", + "1.13.1") val previousVersion = previousVersions.last val previousBinaryCrossVersion = CrossVersion.binaryWith("sjs1_", "") From 3a50066580ac1203f06736539bb033d6e0475a9a Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Sun, 12 Mar 2023 13:01:32 +0100 Subject: [PATCH 02/27] Use a single globalIRCache for testing --- .../test/scala/org/scalajs/linker/testutils/TestIRRepo.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRRepo.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRRepo.scala index a32485b8ce..61fa025c77 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRRepo.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRRepo.scala @@ -19,6 +19,8 @@ import org.scalajs.linker.StandardImpl import org.scalajs.linker.interface.IRFile object TestIRRepo { + private val globalIRCache = StandardImpl.irFileCache() + val minilib: Future[Seq[IRFile]] = load(StdlibHolder.minilib) val javalib: Future[Seq[IRFile]] = load(StdlibHolder.javalib) val empty: Future[Seq[IRFile]] = Future.successful(Nil) @@ -26,7 +28,6 @@ object TestIRRepo { StdlibHolder.previousLibs.map(x => x._1 -> load(x._2)) private def load(stdlibPath: String) = { - val globalIRCache = StandardImpl.irFileCache() Platform.loadJar(stdlibPath) .flatMap(globalIRCache.newCache.cached _) } From ae17b935d1cd388e67c58744d69484d25e4ff78c Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Sun, 12 Mar 2023 13:04:18 +0100 Subject: [PATCH 03/27] Consolidate Info Loading Instead of individual loaders for BaseLinker and Refiner, we consolidate into a single loader. --- .../scalajs/linker/analyzer/Analyzer.scala | 19 +- .../scalajs/linker/analyzer/InfoLoader.scala | 284 ++++++++++++++++++ .../org/scalajs/linker/analyzer/Infos.scala | 43 --- .../scalajs/linker/frontend/BaseLinker.scala | 22 +- .../scalajs/linker/frontend/IRLoader.scala | 99 +----- .../org/scalajs/linker/frontend/Refiner.scala | 252 ++-------------- .../linker/testutils/LinkingUtils.scala | 10 +- 7 files changed, 352 insertions(+), 377 deletions(-) create mode 100644 linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index ac64acd3e1..e02babf77a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -42,7 +42,7 @@ private final class Analyzer(config: CommonPhaseConfig, symbolRequirements: SymbolRequirement, allowAddingSyntheticMethods: Boolean, checkAbstractReachability: Boolean, - inputProvider: Analyzer.InputProvider, + infoLoader: InfoLoader, ec: ExecutionContext) extends Analysis { @@ -85,7 +85,7 @@ private final class Analyzer(config: CommonPhaseConfig, /* Load the java.lang.Object class, and validate it * If it is missing or invalid, we're in deep trouble, and cannot continue. */ - inputProvider.loadInfo(ObjectClass)(ec) match { + infoLoader.loadInfo(ObjectClass)(ec) match { case None => _errors += MissingJavaLangObjectClass(fromAnalyzer) @@ -128,7 +128,7 @@ private final class Analyzer(config: CommonPhaseConfig, reachSymbolRequirement(symbolRequirements) // Reach entry points - for (className <- inputProvider.classesWithEntryPoints()) + for (className <- infoLoader.classesWithEntryPoints()) lookupClass(className)(_.reachEntryPoints()) // Reach module initializers. @@ -350,7 +350,7 @@ private final class Analyzer(config: CommonPhaseConfig, _classInfos(className) = this - inputProvider.loadInfo(className)(ec) match { + infoLoader.loadInfo(className)(ec) match { case Some(future) => workQueue.enqueue(future)(link(_, nonExistent = false)) @@ -1481,19 +1481,12 @@ object Analyzer { symbolRequirements: SymbolRequirement, allowAddingSyntheticMethods: Boolean, checkAbstractReachability: Boolean, - inputProvider: InputProvider)(implicit ec: ExecutionContext): Future[Analysis] = { + infoLoader: InfoLoader)(implicit ec: ExecutionContext): Future[Analysis] = { val analyzer = new Analyzer(config, moduleInitializers, symbolRequirements, - allowAddingSyntheticMethods, checkAbstractReachability, inputProvider, ec) + allowAddingSyntheticMethods, checkAbstractReachability, infoLoader, ec) analyzer.computeReachability().map(_ => analyzer) } - trait InputProvider { - def classesWithEntryPoints(): Iterable[ClassName] - - def loadInfo(className: ClassName)( - implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] - } - private class WorkQueue(ec: ExecutionContext) { private val queue = new ConcurrentLinkedQueue[() => Unit]() private val working = new AtomicBoolean(false) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala new file mode 100644 index 0000000000..51c7f8bae9 --- /dev/null +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala @@ -0,0 +1,284 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.analyzer + +import scala.concurrent._ + +import scala.collection.mutable + +import org.scalajs.ir.{EntryPointsInfo, Version} +import org.scalajs.ir.Names._ +import org.scalajs.ir.Trees._ + +import org.scalajs.logging._ + +import org.scalajs.linker.checker.ClassDefChecker +import org.scalajs.linker.frontend.IRLoader +import org.scalajs.linker.interface.LinkingException +import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps + +final class InfoLoader(irLoader: IRLoader, irCheckMode: InfoLoader.IRCheckMode) { + private var logger: Logger = _ + private val cache = mutable.Map.empty[ClassName, InfoLoader.ClassInfoCache] + + def update(logger: Logger): Unit = { + this.logger = logger + } + + def classesWithEntryPoints(): Iterable[ClassName] = + irLoader.classesWithEntryPoints() + + def loadInfo(className: ClassName)( + implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] = { + if (irLoader.classExists(className)) { + val infoCache = cache.getOrElseUpdate(className, + new InfoLoader.ClassInfoCache(className, irLoader, irCheckMode)) + Some(infoCache.loadInfo(logger)) + } else { + None + } + } + + def cleanAfterRun(): Unit = { + logger = null + cache.filterInPlace((_, infoCache) => infoCache.cleanAfterRun()) + } +} + +object InfoLoader { + sealed trait IRCheckMode + + case object NoIRCheck extends IRCheckMode + case object InitialIRCheck extends IRCheckMode + case object InternalIRCheck extends IRCheckMode + + private class ClassInfoCache(className: ClassName, irLoader: IRLoader, irCheckMode: InfoLoader.IRCheckMode) { + private var cacheUsed: Boolean = false + private var version: Version = Version.Unversioned + private var info: Future[Infos.ClassInfo] = _ + + private val methodsInfoCaches = MethodDefsInfosCache() + private val jsConstructorInfoCache = new JSConstructorDefInfoCache() + private val exportedMembersInfoCaches = JSMethodPropDefsInfosCache() + + def loadInfo(logger: Logger)(implicit ec: ExecutionContext): Future[Infos.ClassInfo] = synchronized { + /* If the cache was already used in this run, the classDef and info are + * already correct, no matter what the versions say. + */ + if (!cacheUsed) { + cacheUsed = true + + val newVersion = irLoader.irFileVersion(className) + if (!version.sameVersion(newVersion)) { + version = newVersion + info = irLoader.loadClassDef(className).map { tree => + irCheckMode match { + case InfoLoader.NoIRCheck => + // no check + + case InfoLoader.InitialIRCheck => + val errorCount = ClassDefChecker.check(tree, + allowReflectiveProxies = false, allowTransients = false, logger) + if (errorCount != 0) { + throw new LinkingException( + s"There were $errorCount ClassDef checking errors.") + } + + case InfoLoader.InternalIRCheck => + val errorCount = ClassDefChecker.check(tree, + allowReflectiveProxies = true, allowTransients = true, logger) + if (errorCount != 0) { + throw new LinkingException( + s"There were $errorCount ClassDef checking errors after optimizing. " + + "Please report this as a bug.") + } + } + + generateInfos(tree) + } + } + } + + info + } + + private def generateInfos(classDef: ClassDef): Infos.ClassInfo = { + val builder = new Infos.ClassInfoBuilder(classDef.className, + classDef.kind, classDef.superClass.map(_.name), + classDef.interfaces.map(_.name), classDef.jsNativeLoadSpec) + + classDef.fields.foreach { + case FieldDef(flags, FieldIdent(name), _, ftpe) => + if (!flags.namespace.isStatic) + builder.maybeAddReferencedFieldClass(name, ftpe) + + case _: JSFieldDef => + // Nothing to do. + } + + classDef.methods.foreach { method => + builder.addMethod(methodsInfoCaches.getInfo(method)) + } + + classDef.jsConstructor.foreach { jsConstructor => + builder.addExportedMember(jsConstructorInfoCache.getInfo(jsConstructor)) + } + + for (info <- exportedMembersInfoCaches.getInfos(classDef.jsMethodProps)) + builder.addExportedMember(info) + + /* We do not cache top-level exports, because they're quite rare, + * and usually quite small when they exist. + */ + classDef.topLevelExportDefs.foreach { topLevelExportDef => + builder.addTopLevelExport(Infos.generateTopLevelExportInfo(classDef.name.name, topLevelExportDef)) + } + + classDef.jsNativeMembers.foreach(builder.addJSNativeMember(_)) + + builder.result() + } + + /** Returns true if the cache has been used and should be kept. */ + def cleanAfterRun(): Boolean = synchronized { + val result = cacheUsed + cacheUsed = false + if (result) { + // No point in cleaning the inner caches if the whole class disappears + methodsInfoCaches.cleanAfterRun() + jsConstructorInfoCache.cleanAfterRun() + exportedMembersInfoCaches.cleanAfterRun() + } + result + } + } + + private final class MethodDefsInfosCache private ( + val caches: Array[mutable.Map[MethodName, MethodDefInfoCache]]) + extends AnyVal { + + def getInfo(methodDef: MethodDef): Infos.MethodInfo = { + val cache = caches(methodDef.flags.namespace.ordinal) + .getOrElseUpdate(methodDef.methodName, new MethodDefInfoCache) + cache.getInfo(methodDef) + } + + def cleanAfterRun(): Unit = { + caches.foreach(_.filterInPlace((_, cache) => cache.cleanAfterRun())) + } + } + + private object MethodDefsInfosCache { + def apply(): MethodDefsInfosCache = { + new MethodDefsInfosCache( + Array.fill(MemberNamespace.Count)(mutable.Map.empty)) + } + } + + /* For JS method and property definitions, we use their index in the list of + * `linkedClass.exportedMembers` as their identity. We cannot use their name + * because the name itself is a `Tree`. + * + * If there is a different number of exported members than in a previous run, + * we always recompute everything. This is fine because, for any given class, + * either all JS methods and properties are reachable, or none are. So we're + * only missing opportunities for incrementality in the case where JS members + * are added or removed in the original .sjsir, which is not a big deal. + */ + private final class JSMethodPropDefsInfosCache private ( + private var caches: Array[JSMethodPropDefInfoCache]) { + + def getInfos(members: List[JSMethodPropDef]): List[Infos.ReachabilityInfo] = { + if (members.isEmpty) { + caches = null + Nil + } else { + val membersSize = members.size + if (caches == null || membersSize != caches.size) + caches = Array.fill(membersSize)(new JSMethodPropDefInfoCache) + + for ((member, i) <- members.zipWithIndex) yield { + caches(i).getInfo(member) + } + } + } + + def cleanAfterRun(): Unit = { + if (caches != null) + caches.foreach(_.cleanAfterRun()) + } + } + + private object JSMethodPropDefsInfosCache { + def apply(): JSMethodPropDefsInfosCache = + new JSMethodPropDefsInfosCache(null) + } + + private abstract class AbstractMemberInfoCache[Def <: VersionedMemberDef, Info] { + private var cacheUsed: Boolean = false + private var lastVersion: Version = Version.Unversioned + private var info: Info = _ + + final def getInfo(member: Def): Info = { + update(member) + info + } + + private final def update(member: Def): Unit = { + if (!cacheUsed) { + cacheUsed = true + val newVersion = member.version + if (!lastVersion.sameVersion(newVersion)) { + info = computeInfo(member) + lastVersion = newVersion + } + } + } + + protected def computeInfo(member: Def): Info + + /** Returns true if the cache has been used and should be kept. */ + final def cleanAfterRun(): Boolean = { + val result = cacheUsed + cacheUsed = false + result + } + } + + private final class MethodDefInfoCache + extends AbstractMemberInfoCache[MethodDef, Infos.MethodInfo] { + + protected def computeInfo(member: MethodDef): Infos.MethodInfo = + Infos.generateMethodInfo(member) + } + + private final class JSConstructorDefInfoCache + extends AbstractMemberInfoCache[JSConstructorDef, Infos.ReachabilityInfo] { + + protected def computeInfo(member: JSConstructorDef): Infos.ReachabilityInfo = + Infos.generateJSConstructorInfo(member) + } + + private final class JSMethodPropDefInfoCache + extends AbstractMemberInfoCache[JSMethodPropDef, Infos.ReachabilityInfo] { + + protected def computeInfo(member: JSMethodPropDef): Infos.ReachabilityInfo = { + member match { + case methodDef: JSMethodDef => + Infos.generateJSMethodInfo(methodDef) + case propertyDef: JSPropertyDef => + Infos.generateJSPropertyInfo(propertyDef) + } + } + } +} diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala index d89b4cb1bb..d063cd5b6a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala @@ -468,49 +468,6 @@ object Infos { else set.toList } - /** Generates the [[ClassInfo]] of a - * [[org.scalajs.ir.Trees.ClassDef Trees.ClassDef]]. - */ - def generateClassInfo(classDef: ClassDef): ClassInfo = { - val builder = new ClassInfoBuilder(classDef.name.name, classDef.kind, - classDef.superClass.map(_.name), classDef.interfaces.map(_.name), - classDef.jsNativeLoadSpec) - - classDef.fields foreach { - case FieldDef(flags, FieldIdent(name), _, ftpe) => - if (!flags.namespace.isStatic) { - builder.maybeAddReferencedFieldClass(name, ftpe) - } - - case _: JSFieldDef => - // Nothing to do. - } - - classDef.methods.foreach { methodDef => - builder.addMethod(generateMethodInfo(methodDef)) - } - - classDef.jsConstructor.foreach { ctorDef => - builder.addExportedMember(generateJSConstructorInfo(ctorDef)) - } - - classDef.jsMethodProps.foreach { - case methodDef: JSMethodDef => - builder.addExportedMember(generateJSMethodInfo(methodDef)) - - case propertyDef: JSPropertyDef => - builder.addExportedMember(generateJSPropertyInfo(propertyDef)) - } - - classDef.jsNativeMembers.foreach(builder.addJSNativeMember(_)) - - classDef.topLevelExportDefs.foreach { topLevelExportDef => - builder.addTopLevelExport(generateTopLevelExportInfo(classDef.name.name, topLevelExportDef)) - } - - builder.result() - } - /** Generates the [[MethodInfo]] of a * [[org.scalajs.ir.Trees.MethodDef Trees.MethodDef]]. */ diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index c18c0ac2e9..47bcba06b0 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -36,8 +36,14 @@ import Analysis._ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { import BaseLinker._ - private val irLoader = new IRLoader(checkIR) + private val irLoader = new FileIRLoader private val methodSynthesizer = new MethodSynthesizer(irLoader) + private val infoLoader = { + new InfoLoader(irLoader, + if (checkIR) InfoLoader.NoIRCheck + else InfoLoader.InitialIRCheck + ) + } def link(irInput: Seq[IRFile], moduleInitializers: Seq[ModuleInitializer], logger: Logger, @@ -45,8 +51,9 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { implicit ec: ExecutionContext): Future[LinkingUnit] = { val result = for { - _ <- irLoader.update(irInput, logger) + _ <- irLoader.update(irInput) analysis <- logger.timeFuture("Linker: Compute reachability") { + infoLoader.update(logger) analyze(moduleInitializers, symbolRequirements, logger) } linkResult <- logger.timeFuture("Linker: Assemble LinkedClasses") { @@ -66,7 +73,10 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { linkResult } - result.andThen { case _ => irLoader.cleanAfterRun() } + result.andThen { case _ => + irLoader.cleanAfterRun() + infoLoader.cleanAfterRun() + } } private def analyze(moduleInitializers: Seq[ModuleInitializer], @@ -94,7 +104,7 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { for { analysis <- Analyzer.computeReachability(config, moduleInitializers, symbolRequirements, allowAddingSyntheticMethods = true, - checkAbstractReachability = true, irLoader) + checkAbstractReachability = true, infoLoader) } yield { if (analysis.errors.nonEmpty) { reportErrors(analysis.errors) @@ -107,11 +117,11 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { private def assemble(moduleInitializers: Seq[ModuleInitializer], analysis: Analysis)(implicit ec: ExecutionContext): Future[LinkingUnit] = { def assembleClass(info: ClassInfo) = { - val classAndVersion = irLoader.loadClassDefAndVersion(info.className) + val version = irLoader.irFileVersion(info.className) val syntheticMethods = methodSynthesizer.synthesizeMembers(info, analysis) for { - (classDef, version) <- classAndVersion + classDef <- irLoader.loadClassDef(info.className) syntheticMethods <- syntheticMethods } yield { BaseLinker.linkClassDef(classDef, version, syntheticMethods, analysis) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala index 536a71b6f5..9c70f40d33 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala @@ -27,17 +27,19 @@ import org.scalajs.ir.Version import org.scalajs.ir.Names.ClassName import org.scalajs.ir.Trees.ClassDef -final class IRLoader(checkIR: Boolean) extends Analyzer.InputProvider - with MethodSynthesizer.InputProvider { +trait IRLoader extends MethodSynthesizer.InputProvider { + def classesWithEntryPoints(): Iterable[ClassName] + def classExists(className: ClassName): Boolean + def irFileVersion(className: ClassName): Version + def loadClassDef(className: ClassName)( + implicit ec: ExecutionContext): Future[ClassDef] +} + +final class FileIRLoader extends IRLoader { private var classNameToFile: collection.Map[ClassName, IRFileImpl] = _ private var entryPoints: collection.Set[ClassName] = _ - private var logger: Logger = _ - private val cache = mutable.Map.empty[ClassName, ClassDefAndInfoCache] - - - def update(irInput: Seq[IRFile], logger: Logger)(implicit ec: ExecutionContext): Future[this.type] = { - this.logger = logger + def update(irInput: Seq[IRFile])(implicit ec: ExecutionContext): Future[this.type] = { Future.traverse(irInput)(i => IRFileImpl.fromIRFile(i).entryPointsInfo).map { infos => val classNameToFile = mutable.Map.empty[ClassName, IRFileImpl] val entryPoints = mutable.Set.empty[ClassName] @@ -60,90 +62,19 @@ final class IRLoader(checkIR: Boolean) extends Analyzer.InputProvider def classesWithEntryPoints(): Iterable[ClassName] = entryPoints - def loadInfo(className: ClassName)( - implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] = { - maybeGet(className, _.classInfo) - } + def classExists(className: ClassName): Boolean = + classNameToFile.contains(className) - def loadClassDefAndVersion(className: ClassName)( - implicit ec: ExecutionContext): Future[(ClassDef, Version)] = { - get(className, u => (u.classDef, u.version)) - } + def irFileVersion(className: ClassName): Version = + classNameToFile(className).version def loadClassDef(className: ClassName)( implicit ec: ExecutionContext): Future[ClassDef] = { - get(className, _.classDef) - } - - private def get[T](className: ClassName, f: ClassDefAndInfoCache.Update => T)( - implicit ec: ExecutionContext): Future[T] = { - maybeGet(className, f).getOrElse { - throw new AssertionError(s"Cannot load file for class $className") - } - } - - private def maybeGet[T](className: ClassName, f: ClassDefAndInfoCache.Update => T)( - implicit ec: ExecutionContext): Option[Future[T]] = { - classNameToFile.get(className).map { irFile => - cache.getOrElseUpdate(className, new ClassDefAndInfoCache) - .update(irFile, logger, checkIR).map(f) - } + classNameToFile(className).tree } def cleanAfterRun(): Unit = { classNameToFile = null entryPoints = null - logger = null - cache.filterInPlace((_, fileCache) => fileCache.cleanAfterRun()) - } -} - -private object ClassDefAndInfoCache { - final class Update( - val classDef: ClassDef, - val classInfo: Infos.ClassInfo, - val version: Version) -} - -private final class ClassDefAndInfoCache { - import ClassDefAndInfoCache.Update - - private var cacheUsed: Boolean = false - private var version: Version = Version.Unversioned - private var cacheUpdate: Future[Update] = _ - - def update(irFile: IRFileImpl, logger: Logger, checkIR: Boolean)( - implicit ec: ExecutionContext): Future[Update] = synchronized { - /* If the cache was already used in this run, the classDef and info are - * already correct, no matter what the versions say. - */ - if (!cacheUsed) { - cacheUsed = true - - val newVersion = irFile.version - if (!version.sameVersion(newVersion)) { - version = newVersion - cacheUpdate = irFile.tree.map { tree => - if (checkIR) { - val errorCount = ClassDefChecker.check(tree, - allowReflectiveProxies = false, allowTransients = false, logger) - if (errorCount != 0) { - throw new LinkingException( - s"There were $errorCount ClassDef checking errors.") - } - } - new Update(tree, Infos.generateClassInfo(tree), version) - } - } - } - - cacheUpdate - } - - /** Returns true if the cache has been used and should be kept. */ - def cleanAfterRun(): Boolean = synchronized { - val result = cacheUsed - cacheUsed = false - result } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala index 729c329093..0c555f5fad 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala @@ -14,35 +14,36 @@ package org.scalajs.linker.frontend import scala.concurrent._ -import scala.collection.mutable - import org.scalajs.ir.{EntryPointsInfo, Version} -import org.scalajs.ir.Names._ -import org.scalajs.ir.Trees._ +import org.scalajs.ir.Names.ClassName +import org.scalajs.ir.Trees.ClassDef import org.scalajs.logging._ -import org.scalajs.linker._ -import org.scalajs.linker.checker.ClassDefChecker import org.scalajs.linker.interface.ModuleInitializer import org.scalajs.linker.standard._ import org.scalajs.linker.standard.ModuleSet.ModuleID import org.scalajs.linker.analyzer._ -import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps /** Does a dead code elimination pass on a [[LinkingUnit]]. */ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { import Refiner._ - private val inputProvider = new InputProvider(checkIR) + private val irLoader = new ClassDefIRLoader + private val infoLoader = { + new InfoLoader(irLoader, + if (checkIR) InfoLoader.NoIRCheck + else InfoLoader.InternalIRCheck + ) + } def refine(classDefs: Seq[(ClassDef, Version)], moduleInitializers: List[ModuleInitializer], symbolRequirements: SymbolRequirement, logger: Logger)( implicit ec: ExecutionContext): Future[LinkingUnit] = { - val linkedClassesByName = classDefs.map(c => c._1.className -> c._1).toMap - inputProvider.update(linkedClassesByName, logger) + irLoader.update(classDefs) + infoLoader.update(logger) val analysis = logger.timeFuture("Refiner: Compute reachability") { analyze(moduleInitializers, symbolRequirements, logger) @@ -66,7 +67,8 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { linkedTopLevelExports.flatten.toList, moduleInitializers) } - inputProvider.cleanAfterRun() + irLoader.cleanAfterRun() + infoLoader.cleanAfterRun() result } @@ -78,7 +80,7 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { for { analysis <- Analyzer.computeReachability(config, moduleInitializers, symbolRequirements, allowAddingSyntheticMethods = false, - checkAbstractReachability = false, inputProvider) + checkAbstractReachability = false, infoLoader) } yield { /* There must not be linking errors at this point. If there are, it is a * bug in the optimizer. @@ -99,14 +101,11 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { } private object Refiner { - private class InputProvider(checkIR: Boolean) extends Analyzer.InputProvider { + private final class ClassDefIRLoader extends IRLoader { private var classesByName: Map[ClassName, ClassDef] = _ - private var logger: Logger = _ - private val cache = mutable.Map.empty[ClassName, ClassInfoCache] - def update(classesByName: Map[ClassName, ClassDef], logger: Logger): Unit = { - this.logger = logger - this.classesByName = classesByName + def update(classDefs: Seq[(ClassDef, Version)]): Unit = { + this.classesByName = classDefs.map(c => c._1.className -> c._1).toMap } def classesWithEntryPoints(): Iterable[ClassName] = { @@ -115,220 +114,19 @@ private object Refiner { .map(_.className) } - def loadInfo(className: ClassName)(implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] = - getCache(className).map(_.loadInfo(classesByName(className), logger)) - - private def getCache(className: ClassName): Option[ClassInfoCache] = { - cache.get(className).orElse { - if (classesByName.contains(className)) { - val fileCache = new ClassInfoCache(checkIR) - cache += className -> fileCache - Some(fileCache) - } else { - None - } - } - } - - def cleanAfterRun(): Unit = { - classesByName = null - cache.filterInPlace((_, linkedClassCache) => linkedClassCache.cleanAfterRun()) - } - } + def classExists(className: ClassName): Boolean = + classesByName.contains(className) - private class ClassInfoCache(checkIR: Boolean) { - private var cacheUsed: Boolean = false - private val methodsInfoCaches = MethodDefsInfosCache() - private val jsConstructorInfoCache = new JSConstructorDefInfoCache() - private val exportedMembersInfoCaches = JSMethodPropDefsInfosCache() - private var info: Infos.ClassInfo = _ + def irFileVersion(className: ClassName): Version = + Version.Unversioned - def loadInfo(classDef: ClassDef, logger: Logger)(implicit ec: ExecutionContext): Future[Infos.ClassInfo] = Future { - update(classDef, logger) - info - } - - private def update(classDef: ClassDef, logger: Logger): Unit = synchronized { - if (!cacheUsed) { - cacheUsed = true - - if (checkIR) { - val errorCount = ClassDefChecker.check(classDef, - allowReflectiveProxies = true, allowTransients = true, logger) - if (errorCount != 0) { - throw new AssertionError( - s"There were $errorCount ClassDef checking errors after optimizing. " + - "Please report this as a bug.") - } - } - - val builder = new Infos.ClassInfoBuilder(classDef.className, - classDef.kind, classDef.superClass.map(_.name), - classDef.interfaces.map(_.name), classDef.jsNativeLoadSpec) - - classDef.fields.foreach { - case FieldDef(flags, FieldIdent(name), _, ftpe) => - if (!flags.namespace.isStatic) - builder.maybeAddReferencedFieldClass(name, ftpe) - - case _: JSFieldDef => - // Nothing to do. - } - - classDef.methods.foreach { method => - builder.addMethod(methodsInfoCaches.getInfo(method)) - } - - classDef.jsConstructor.foreach { jsConstructor => - builder.addExportedMember(jsConstructorInfoCache.getInfo(jsConstructor)) - } - - for (info <- exportedMembersInfoCaches.getInfos(classDef.jsMethodProps)) - builder.addExportedMember(info) - - /* We do not cache top-level exports, because they're quite rare, - * and usually quite small when they exist. - */ - classDef.topLevelExportDefs.foreach { topLevelExportDef => - builder.addTopLevelExport(Infos.generateTopLevelExportInfo(classDef.name.name, topLevelExportDef)) - } - - classDef.jsNativeMembers.foreach(builder.addJSNativeMember(_)) - - info = builder.result() - } - } - - /** Returns true if the cache has been used and should be kept. */ - def cleanAfterRun(): Boolean = synchronized { - val result = cacheUsed - cacheUsed = false - if (result) { - // No point in cleaning the inner caches if the whole class disappears - methodsInfoCaches.cleanAfterRun() - jsConstructorInfoCache.cleanAfterRun() - exportedMembersInfoCaches.cleanAfterRun() - } - result - } - } - - private final class MethodDefsInfosCache private ( - val caches: Array[mutable.Map[MethodName, MethodDefInfoCache]]) - extends AnyVal { - - def getInfo(methodDef: MethodDef): Infos.MethodInfo = { - val cache = caches(methodDef.flags.namespace.ordinal) - .getOrElseUpdate(methodDef.methodName, new MethodDefInfoCache) - cache.getInfo(methodDef) + def loadClassDef(className: ClassName)( + implicit ec: ExecutionContext): Future[ClassDef] = { + Future.successful(classesByName(className)) } def cleanAfterRun(): Unit = { - caches.foreach(_.filterInPlace((_, cache) => cache.cleanAfterRun())) - } - } - - private object MethodDefsInfosCache { - def apply(): MethodDefsInfosCache = { - new MethodDefsInfosCache( - Array.fill(MemberNamespace.Count)(mutable.Map.empty)) - } - } - - /* For JS method and property definitions, we use their index in the list of - * `linkedClass.exportedMembers` as their identity. We cannot use their name - * because the name itself is a `Tree`. - * - * If there is a different number of exported members than in a previous run, - * we always recompute everything. This is fine because, for any given class, - * either all JS methods and properties are reachable, or none are. So we're - * only missing opportunities for incrementality in the case where JS members - * are added or removed in the original .sjsir, which is not a big deal. - */ - private final class JSMethodPropDefsInfosCache private ( - private var caches: Array[JSMethodPropDefInfoCache]) { - - def getInfos(members: List[JSMethodPropDef]): List[Infos.ReachabilityInfo] = { - if (members.isEmpty) { - caches = null - Nil - } else { - val membersSize = members.size - if (caches == null || membersSize != caches.size) - caches = Array.fill(membersSize)(new JSMethodPropDefInfoCache) - - for ((member, i) <- members.zipWithIndex) yield { - caches(i).getInfo(member) - } - } - } - - def cleanAfterRun(): Unit = { - if (caches != null) - caches.foreach(_.cleanAfterRun()) - } - } - - private object JSMethodPropDefsInfosCache { - def apply(): JSMethodPropDefsInfosCache = - new JSMethodPropDefsInfosCache(null) - } - - private abstract class AbstractMemberInfoCache[Def <: VersionedMemberDef, Info] { - private var cacheUsed: Boolean = false - private var lastVersion: Version = Version.Unversioned - private var info: Info = _ - - final def getInfo(member: Def): Info = { - update(member) - info - } - - private final def update(member: Def): Unit = { - if (!cacheUsed) { - cacheUsed = true - val newVersion = member.version - if (!lastVersion.sameVersion(newVersion)) { - info = computeInfo(member) - lastVersion = newVersion - } - } - } - - protected def computeInfo(member: Def): Info - - /** Returns true if the cache has been used and should be kept. */ - final def cleanAfterRun(): Boolean = { - val result = cacheUsed - cacheUsed = false - result - } - } - - private final class MethodDefInfoCache - extends AbstractMemberInfoCache[MethodDef, Infos.MethodInfo] { - - protected def computeInfo(member: MethodDef): Infos.MethodInfo = - Infos.generateMethodInfo(member) - } - - private final class JSConstructorDefInfoCache - extends AbstractMemberInfoCache[JSConstructorDef, Infos.ReachabilityInfo] { - - protected def computeInfo(member: JSConstructorDef): Infos.ReachabilityInfo = - Infos.generateJSConstructorInfo(member) - } - - private final class JSMethodPropDefInfoCache - extends AbstractMemberInfoCache[JSMethodPropDef, Infos.ReachabilityInfo] { - - protected def computeInfo(member: JSMethodPropDef): Infos.ReachabilityInfo = { - member match { - case methodDef: JSMethodDef => - Infos.generateJSMethodInfo(methodDef) - case propertyDef: JSPropertyDef => - Infos.generateJSPropertyInfo(propertyDef) - } + classesByName = null } } } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala index ef09ab821c..b277b9d750 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala @@ -20,7 +20,7 @@ import org.scalajs.logging._ import org.scalajs.linker._ import org.scalajs.linker.analyzer._ -import org.scalajs.linker.frontend.IRLoader +import org.scalajs.linker.frontend.FileIRLoader import org.scalajs.linker.interface._ import org.scalajs.linker.standard._ @@ -100,16 +100,18 @@ object LinkingUtils { val classDefIRFiles = classDefs.map(MemClassDefIRFile(_)) val injectedIRFiles = StandardLinkerBackend(config).injectedIRFiles - val loader = new IRLoader(checkIR = true) + val irLoader = new FileIRLoader + val infoLoader = new InfoLoader(irLoader, InfoLoader.InitialIRCheck) val logger = new ScalaConsoleLogger(Level.Error) for { baseFiles <- stdlib - irLoader <- loader.update(classDefIRFiles ++ baseFiles ++ injectedIRFiles, logger) + _ <- irLoader.update(classDefIRFiles ++ baseFiles ++ injectedIRFiles) + _ = infoLoader.update(logger) analysis <- Analyzer.computeReachability( CommonPhaseConfig.fromStandardConfig(config), moduleInitializers, symbolRequirements, allowAddingSyntheticMethods = true, - checkAbstractReachability = true, irLoader) + checkAbstractReachability = true, infoLoader) } yield { analysis } From 390d92ddb40baed4979d762e2b427c7ffd1bed89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Sun, 16 Apr 2023 13:52:34 +0200 Subject: [PATCH 04/27] Fix conditions to enable ClassDef checking in `InfoLoader`. --- .../scalajs/linker/frontend/BaseLinker.scala | 4 +-- .../org/scalajs/linker/frontend/Refiner.scala | 4 +-- .../linker/checker/ClassDefCheckerTest.scala | 35 +++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index 47bcba06b0..f2164f23a0 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -40,8 +40,8 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { private val methodSynthesizer = new MethodSynthesizer(irLoader) private val infoLoader = { new InfoLoader(irLoader, - if (checkIR) InfoLoader.NoIRCheck - else InfoLoader.InitialIRCheck + if (checkIR) InfoLoader.InitialIRCheck + else InfoLoader.NoIRCheck ) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala index 0c555f5fad..681a2c409e 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala @@ -32,8 +32,8 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { private val irLoader = new ClassDefIRLoader private val infoLoader = { new InfoLoader(irLoader, - if (checkIR) InfoLoader.NoIRCheck - else InfoLoader.InternalIRCheck + if (checkIR) InfoLoader.InternalIRCheck + else InfoLoader.NoIRCheck ) } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala index 6b5f82190d..d092a04ee4 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala @@ -21,11 +21,46 @@ import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.Trees._ import org.scalajs.ir.Types._ +import org.scalajs.logging.NullLogger + +import org.scalajs.linker.interface.{LinkingException, StandardConfig} +import org.scalajs.linker.standard.{StandardLinkerFrontend, SymbolRequirement} + +import org.scalajs.linker.testutils._ import org.scalajs.linker.testutils.TestIRBuilder._ +import org.scalajs.junit.async.{AsyncResult, await} + class ClassDefCheckerTest { import ClassDefCheckerTest.assertError + @Test + def linkerActuallyFailsOnClassDefCheckerError(): AsyncResult = await { + import scala.concurrent.ExecutionContext.Implicits.global + + val wrongClassDef = classDef( + "A", + kind = ClassKind.Interface, + jsNativeLoadSpec = Some(JSNativeLoadSpec.Global("Foo", Nil)) + ) + + val config = StandardConfig() + .withCheckIR(true) + .withOptimizer(false) + val linkerFrontend = StandardLinkerFrontend(config) + + val loadASymbolRequirements = SymbolRequirement + .factory("ClassDefCheckerTest") + .classData("A") + + TestIRRepo.minilib.flatMap { stdLibFiles => + val irFiles = stdLibFiles :+ MemClassDefIRFile(wrongClassDef) + linkerFrontend.link(irFiles, Nil, loadASymbolRequirements, NullLogger) + }.failed.map { th => + assertTrue(th.toString(), th.isInstanceOf[LinkingException]) + } + } + @Test def javaLangObjectNoSuperClass(): Unit = { assertError( From 9302a39bb30b0bda84952da7cfc3a932d021f933 Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Sun, 19 Feb 2023 17:28:42 +0100 Subject: [PATCH 05/27] Keep the analyzer alive over incremental runs - Prepares for incremental analysis. - Allows us to move the InfoLoader into the Analyzer. - Overall more consistent. --- .../scalajs/linker/analyzer/Analyzer.scala | 88 ++++++++++--------- .../scalajs/linker/analyzer/InfoLoader.scala | 4 +- .../scalajs/linker/frontend/BaseLinker.scala | 14 +-- .../org/scalajs/linker/frontend/Refiner.scala | 14 +-- .../linker/testutils/LinkingUtils.scala | 9 +- 5 files changed, 59 insertions(+), 70 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index e02babf77a..c9d75c4cc8 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -29,25 +29,33 @@ import org.scalajs.ir.Trees.{MemberNamespace, JSNativeLoadSpec} import org.scalajs.ir.Types.ClassRef import org.scalajs.linker._ +import org.scalajs.linker.frontend.IRLoader import org.scalajs.linker.interface.{ESVersion, ModuleKind, ModuleInitializer} import org.scalajs.linker.interface.unstable.ModuleInitializerImpl import org.scalajs.linker.standard._ import org.scalajs.linker.standard.ModuleSet.ModuleID +import org.scalajs.logging.Logger + import Analysis._ import Infos.{NamespacedMethodName, ReachabilityInfo, ReachabilityInfoInClass} -private final class Analyzer(config: CommonPhaseConfig, - moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, - allowAddingSyntheticMethods: Boolean, - checkAbstractReachability: Boolean, - infoLoader: InfoLoader, - ec: ExecutionContext) - extends Analysis { +final class Analyzer(config: CommonPhaseConfig, initial: Boolean, + checkIR: Boolean, irLoader: IRLoader) { import Analyzer._ + private val allowAddingSyntheticMethods = initial + private val checkAbstractReachability = initial + + private val infoLoader: InfoLoader = { + new InfoLoader(irLoader, + if (!checkIR) InfoLoader.NoIRCheck + else if (initial) InfoLoader.InitialIRCheck + else InfoLoader.InternalIRCheck + ) + } + private val isNoModule = config.coreSpec.moduleKind == ModuleKind.NoModule private var objectClassInfo: ClassInfo = _ @@ -55,28 +63,34 @@ private final class Analyzer(config: CommonPhaseConfig, private[this] val _errors = mutable.Buffer.empty[Error] - private val workQueue = new WorkQueue(ec) + private var workQueue: WorkQueue = _ private val fromAnalyzer = FromCore("analyzer") - private[this] var _loadedClassInfos: scala.collection.Map[ClassName, ClassInfo] = _ + private[this] val _topLevelExportInfos = mutable.Map.empty[(ModuleID, String), TopLevelExportInfo] - def classInfos: scala.collection.Map[ClassName, Analysis.ClassInfo] = - _loadedClassInfos + def computeReachability(moduleInitializers: Seq[ModuleInitializer], + symbolRequirements: SymbolRequirement, logger: Logger)(implicit ec: ExecutionContext): Future[Analysis] = { - private[this] val _topLevelExportInfos = mutable.Map.empty[(ModuleID, String), TopLevelExportInfo] + resetState() - def topLevelExportInfos: scala.collection.Map[(ModuleID, String), Analysis.TopLevelExportInfo] = - _topLevelExportInfos + infoLoader.update(logger) - def errors: scala.collection.Seq[Error] = _errors + workQueue = new WorkQueue(ec) - def computeReachability(): Future[Unit] = { - require(_classInfos.isEmpty, "Cannot run the same Analyzer multiple times") + loadObjectClass(() => loadEverything(moduleInitializers, symbolRequirements)) - loadObjectClass(() => loadEverything()) + workQueue.join() + .map(_ => postLoad(moduleInitializers))(ec) + .andThen { case _ => infoLoader.cleanAfterRun() } + } - workQueue.join().map(_ => postLoad())(ec) + private def resetState(): Unit = { + objectClassInfo = null + workQueue = null + _errors.clear() + _classInfos.clear() + _topLevelExportInfos.clear() } private def loadObjectClass(onSuccess: () => Unit): Unit = { @@ -85,7 +99,7 @@ private final class Analyzer(config: CommonPhaseConfig, /* Load the java.lang.Object class, and validate it * If it is missing or invalid, we're in deep trouble, and cannot continue. */ - infoLoader.loadInfo(ObjectClass)(ec) match { + infoLoader.loadInfo(ObjectClass)(workQueue.ec) match { case None => _errors += MissingJavaLangObjectClass(fromAnalyzer) @@ -101,7 +115,8 @@ private final class Analyzer(config: CommonPhaseConfig, } } - private def loadEverything(): Unit = { + private def loadEverything(moduleInitializers: Seq[ModuleInitializer], + symbolRequirements: SymbolRequirement): Unit = { assert(objectClassInfo != null) implicit val from = fromAnalyzer @@ -135,11 +150,11 @@ private final class Analyzer(config: CommonPhaseConfig, reachInitializers(moduleInitializers) } - private def postLoad(): Unit = { + private def postLoad(moduleInitializers: Seq[ModuleInitializer]): Analysis = { if (isNoModule) { // Check there is only a single module. val publicModuleIDs = ( - topLevelExportInfos.keys.map(_._1).toList ++ + _topLevelExportInfos.keys.map(_._1).toList ++ moduleInitializers.map(i => ModuleID(i.moduleID)) ).distinct @@ -153,10 +168,14 @@ private final class Analyzer(config: CommonPhaseConfig, assert(_errors.nonEmpty || infos.size == _classInfos.size, "unloaded classes in post load phase") - _loadedClassInfos = infos - // Reach additional data, based on reflection methods used reachDataThroughReflection(infos) + + new Analysis { + val classInfos = infos + val topLevelExportInfos = _topLevelExportInfos + val errors = _errors + } } private def reachSymbolRequirement(requirement: SymbolRequirement, @@ -350,7 +369,7 @@ private final class Analyzer(config: CommonPhaseConfig, _classInfos(className) = this - infoLoader.loadInfo(className)(ec) match { + infoLoader.loadInfo(className)(workQueue.ec) match { case Some(future) => workQueue.enqueue(future)(link(_, nonExistent = false)) @@ -858,7 +877,7 @@ private final class Analyzer(config: CommonPhaseConfig, // Starting here, we just do data juggling, so it can run on any thread. locally { - implicit val iec = ec + implicit val iec = workQueue.ec val hasMoreSpecific = Future.traverse(specificityChecks)( checks => Future.sequence(checks).map(_.contains(true))) @@ -1476,18 +1495,7 @@ object Analyzer { private val getSuperclassMethodName = MethodName("getSuperclass", Nil, ClassRef(ClassClass)) - def computeReachability(config: CommonPhaseConfig, - moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, - allowAddingSyntheticMethods: Boolean, - checkAbstractReachability: Boolean, - infoLoader: InfoLoader)(implicit ec: ExecutionContext): Future[Analysis] = { - val analyzer = new Analyzer(config, moduleInitializers, symbolRequirements, - allowAddingSyntheticMethods, checkAbstractReachability, infoLoader, ec) - analyzer.computeReachability().map(_ => analyzer) - } - - private class WorkQueue(ec: ExecutionContext) { + private class WorkQueue(val ec: ExecutionContext) { private val queue = new ConcurrentLinkedQueue[() => Unit]() private val working = new AtomicBoolean(false) private val pending = new AtomicInteger(0) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala index 51c7f8bae9..d0b210c47f 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala @@ -27,7 +27,7 @@ import org.scalajs.linker.frontend.IRLoader import org.scalajs.linker.interface.LinkingException import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps -final class InfoLoader(irLoader: IRLoader, irCheckMode: InfoLoader.IRCheckMode) { +private[analyzer] final class InfoLoader(irLoader: IRLoader, irCheckMode: InfoLoader.IRCheckMode) { private var logger: Logger = _ private val cache = mutable.Map.empty[ClassName, InfoLoader.ClassInfoCache] @@ -55,7 +55,7 @@ final class InfoLoader(irLoader: IRLoader, irCheckMode: InfoLoader.IRCheckMode) } } -object InfoLoader { +private[analyzer] object InfoLoader { sealed trait IRCheckMode case object NoIRCheck extends IRCheckMode diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index f2164f23a0..9bbaf41854 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -37,13 +37,9 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { import BaseLinker._ private val irLoader = new FileIRLoader + private val analyzer = + new Analyzer(config, initial = true, checkIR = checkIR, irLoader) private val methodSynthesizer = new MethodSynthesizer(irLoader) - private val infoLoader = { - new InfoLoader(irLoader, - if (checkIR) InfoLoader.InitialIRCheck - else InfoLoader.NoIRCheck - ) - } def link(irInput: Seq[IRFile], moduleInitializers: Seq[ModuleInitializer], logger: Logger, @@ -53,7 +49,6 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { val result = for { _ <- irLoader.update(irInput) analysis <- logger.timeFuture("Linker: Compute reachability") { - infoLoader.update(logger) analyze(moduleInitializers, symbolRequirements, logger) } linkResult <- logger.timeFuture("Linker: Assemble LinkedClasses") { @@ -75,7 +70,6 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { result.andThen { case _ => irLoader.cleanAfterRun() - infoLoader.cleanAfterRun() } } @@ -102,9 +96,7 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { } for { - analysis <- Analyzer.computeReachability(config, moduleInitializers, - symbolRequirements, allowAddingSyntheticMethods = true, - checkAbstractReachability = true, infoLoader) + analysis <- analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } yield { if (analysis.errors.nonEmpty) { reportErrors(analysis.errors) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala index 681a2c409e..2e16571f90 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala @@ -30,12 +30,8 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { import Refiner._ private val irLoader = new ClassDefIRLoader - private val infoLoader = { - new InfoLoader(irLoader, - if (checkIR) InfoLoader.InternalIRCheck - else InfoLoader.NoIRCheck - ) - } + private val analyzer = + new Analyzer(config, initial = false, checkIR = checkIR, irLoader) def refine(classDefs: Seq[(ClassDef, Version)], moduleInitializers: List[ModuleInitializer], @@ -43,7 +39,6 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { implicit ec: ExecutionContext): Future[LinkingUnit] = { irLoader.update(classDefs) - infoLoader.update(logger) val analysis = logger.timeFuture("Refiner: Compute reachability") { analyze(moduleInitializers, symbolRequirements, logger) @@ -68,7 +63,6 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { } irLoader.cleanAfterRun() - infoLoader.cleanAfterRun() result } @@ -78,9 +72,7 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { symbolRequirements: SymbolRequirement, logger: Logger)( implicit ec: ExecutionContext): Future[Analysis] = { for { - analysis <- Analyzer.computeReachability(config, moduleInitializers, - symbolRequirements, allowAddingSyntheticMethods = false, - checkAbstractReachability = false, infoLoader) + analysis <- analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } yield { /* There must not be linking errors at this point. If there are, it is a * bug in the optimizer. diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala index b277b9d750..a05f14fe11 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala @@ -101,17 +101,14 @@ object LinkingUtils { val injectedIRFiles = StandardLinkerBackend(config).injectedIRFiles val irLoader = new FileIRLoader - val infoLoader = new InfoLoader(irLoader, InfoLoader.InitialIRCheck) + val analyzer = new Analyzer(CommonPhaseConfig.fromStandardConfig(config), + initial = true, checkIR = true, irLoader) val logger = new ScalaConsoleLogger(Level.Error) for { baseFiles <- stdlib _ <- irLoader.update(classDefIRFiles ++ baseFiles ++ injectedIRFiles) - _ = infoLoader.update(logger) - analysis <- Analyzer.computeReachability( - CommonPhaseConfig.fromStandardConfig(config), moduleInitializers, - symbolRequirements, allowAddingSyntheticMethods = true, - checkAbstractReachability = true, infoLoader) + analysis <- analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } yield { analysis } From 937b3a3c82ada8040cbe49c7d1550de5a824d06b Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Sun, 9 Apr 2023 18:59:50 +0200 Subject: [PATCH 06/27] Move error logging into the Analyzer Now that the Analyzer needs a Logger and has a concept of "initial" analysis, this seems simpler. --- .../scalajs/linker/analyzer/Analyzer.scala | 44 ++++++++++++++++--- .../scalajs/linker/frontend/BaseLinker.scala | 38 +--------------- .../org/scalajs/linker/frontend/Refiner.scala | 27 +----------- .../linker/testutils/LinkingUtils.scala | 2 +- 4 files changed, 43 insertions(+), 68 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index c9d75c4cc8..e16f1a40be 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -17,7 +17,7 @@ import scala.annotation.tailrec import scala.collection.mutable import scala.concurrent._ -import scala.util.{Success, Failure} +import scala.util.{Try, Success, Failure} import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} @@ -30,18 +30,18 @@ import org.scalajs.ir.Types.ClassRef import org.scalajs.linker._ import org.scalajs.linker.frontend.IRLoader -import org.scalajs.linker.interface.{ESVersion, ModuleKind, ModuleInitializer} +import org.scalajs.linker.interface._ import org.scalajs.linker.interface.unstable.ModuleInitializerImpl import org.scalajs.linker.standard._ import org.scalajs.linker.standard.ModuleSet.ModuleID -import org.scalajs.logging.Logger +import org.scalajs.logging._ import Analysis._ import Infos.{NamespacedMethodName, ReachabilityInfo, ReachabilityInfoInClass} final class Analyzer(config: CommonPhaseConfig, initial: Boolean, - checkIR: Boolean, irLoader: IRLoader) { + checkIR: Boolean, failOnError: Boolean, irLoader: IRLoader) { import Analyzer._ @@ -81,7 +81,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, loadObjectClass(() => loadEverything(moduleInitializers, symbolRequirements)) workQueue.join() - .map(_ => postLoad(moduleInitializers))(ec) + .map(_ => postLoad(moduleInitializers, logger))(ec) .andThen { case _ => infoLoader.cleanAfterRun() } } @@ -150,7 +150,8 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, reachInitializers(moduleInitializers) } - private def postLoad(moduleInitializers: Seq[ModuleInitializer]): Analysis = { + private def postLoad(moduleInitializers: Seq[ModuleInitializer], + logger: Logger): Analysis = { if (isNoModule) { // Check there is only a single module. val publicModuleIDs = ( @@ -171,6 +172,9 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, // Reach additional data, based on reflection methods used reachDataThroughReflection(infos) + if (failOnError && _errors.nonEmpty) + reportErrors(logger) + new Analysis { val classInfos = infos val topLevelExportInfos = _topLevelExportInfos @@ -178,6 +182,34 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } } + private def reportErrors(logger: Logger): Unit = { + require(_errors.nonEmpty) + + val maxDisplayErrors = { + val propName = "org.scalajs.linker.maxlinkingerrors" + Try(System.getProperty(propName, "20").toInt).getOrElse(20).max(1) + } + + _errors + .take(maxDisplayErrors) + .foreach(logError(_, logger, Level.Error)) + + val skipped = _errors.size - maxDisplayErrors + if (skipped > 0) + logger.log(Level.Error, s"Not showing $skipped more linking errors") + + if (initial) { + throw new LinkingException("There were linking errors") + } else { + throw new AssertionError( + "There were linking errors after the optimizer has run. " + + "This is a bug, please report it. " + + "You can work around the bug by disabling the optimizer. " + + "In the sbt plugin, this can be done with " + + "`scalaJSLinkerConfig ~= { _.withOptimizer(false) }`.") + } + } + private def reachSymbolRequirement(requirement: SymbolRequirement, optional: Boolean = false): Unit = { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index 9bbaf41854..7589e8b651 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -13,7 +13,6 @@ package org.scalajs.linker.frontend import scala.concurrent._ -import scala.util.Try import org.scalajs.logging._ @@ -38,7 +37,7 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { private val irLoader = new FileIRLoader private val analyzer = - new Analyzer(config, initial = true, checkIR = checkIR, irLoader) + new Analyzer(config, initial = true, checkIR = checkIR, failOnError = true, irLoader) private val methodSynthesizer = new MethodSynthesizer(irLoader) def link(irInput: Seq[IRFile], @@ -49,7 +48,7 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { val result = for { _ <- irLoader.update(irInput) analysis <- logger.timeFuture("Linker: Compute reachability") { - analyze(moduleInitializers, symbolRequirements, logger) + analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } linkResult <- logger.timeFuture("Linker: Assemble LinkedClasses") { assemble(moduleInitializers, analysis) @@ -73,39 +72,6 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { } } - private def analyze(moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, logger: Logger)( - implicit ec: ExecutionContext): Future[Analysis] = { - def reportErrors(errors: scala.collection.Seq[Analysis.Error]) = { - require(errors.nonEmpty) - - val maxDisplayErrors = { - val propName = "org.scalajs.linker.maxlinkingerrors" - Try(System.getProperty(propName, "20").toInt).getOrElse(20).max(1) - } - - errors - .take(maxDisplayErrors) - .foreach(logError(_, logger, Level.Error)) - - val skipped = errors.size - maxDisplayErrors - if (skipped > 0) - logger.log(Level.Error, s"Not showing $skipped more linking errors") - - throw new LinkingException("There were linking errors") - } - - for { - analysis <- analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) - } yield { - if (analysis.errors.nonEmpty) { - reportErrors(analysis.errors) - } - - analysis - } - } - private def assemble(moduleInitializers: Seq[ModuleInitializer], analysis: Analysis)(implicit ec: ExecutionContext): Future[LinkingUnit] = { def assembleClass(info: ClassInfo) = { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala index 2e16571f90..985023e7ae 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala @@ -31,7 +31,7 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { private val irLoader = new ClassDefIRLoader private val analyzer = - new Analyzer(config, initial = false, checkIR = checkIR, irLoader) + new Analyzer(config, initial = false, checkIR = checkIR, failOnError = true, irLoader) def refine(classDefs: Seq[(ClassDef, Version)], moduleInitializers: List[ModuleInitializer], @@ -41,7 +41,7 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { irLoader.update(classDefs) val analysis = logger.timeFuture("Refiner: Compute reachability") { - analyze(moduleInitializers, symbolRequirements, logger) + analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } for { @@ -67,29 +67,6 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { result } } - - private def analyze(moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, logger: Logger)( - implicit ec: ExecutionContext): Future[Analysis] = { - for { - analysis <- analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) - } yield { - /* There must not be linking errors at this point. If there are, it is a - * bug in the optimizer. - */ - if (analysis.errors.isEmpty) { - analysis - } else { - analysis.errors.foreach(Analysis.logError(_, logger, Level.Error)) - throw new AssertionError( - "There were linking errors after the optimizer has run. " + - "This is a bug, please report it. " + - "You can work around the bug by disabling the optimizer. " + - "In the sbt plugin, this can be done with " + - "`scalaJSLinkerConfig ~= { _.withOptimizer(false) }`.") - } - } - } } private object Refiner { diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala index a05f14fe11..f3854a7a76 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala @@ -102,7 +102,7 @@ object LinkingUtils { val irLoader = new FileIRLoader val analyzer = new Analyzer(CommonPhaseConfig.fromStandardConfig(config), - initial = true, checkIR = true, irLoader) + initial = true, checkIR = true, failOnError = false, irLoader) val logger = new ScalaConsoleLogger(Level.Error) for { From 97a11ec3e5149adf7dd68f5d75f9ec3ab5dc74b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 4 May 2023 09:54:55 +0200 Subject: [PATCH 07/27] Fix a compiler warning coming in 2.13.11. `name` was both inherited from the superclass, and accessible in the outer scope. Such bindings become ambiguous in Scala 3, and Scala 2.13.11 will warn about them. --- .../src/main/scala/org/scalajs/linker/MemOutputFile.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/MemOutputFile.scala b/linker/shared/src/main/scala/org/scalajs/linker/MemOutputFile.scala index f9ead3479f..54dceb18d2 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/MemOutputFile.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/MemOutputFile.scala @@ -26,12 +26,12 @@ sealed trait MemOutputFile extends LinkerOutput.File { @deprecated("Part of old Linker interface. Use MemOutputDirectory instead.", "1.3.0") object MemOutputFile { - private final val name = "mem-file.js" + private final val MemFileName = "mem-file.js" def apply(): MemOutputFile = new Impl(MemOutputDirectory()) private final class Impl(dir: MemOutputDirectory) - extends OutputFileImpl(name, dir) with MemOutputFile { + extends OutputFileImpl(MemFileName, dir) with MemOutputFile { def content: Array[Byte] = { dir.content(name).getOrElse { throw new IllegalStateException("content hasn't been written yet") From 003a60f74acf9ca587a0e7e7269b78f43fe59a34 Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Fri, 12 May 2023 21:39:44 +0200 Subject: [PATCH 08/27] Safer and fixed cache invalidation in Emitter We never pass a full LinkedClass anymore to a generating function under cache. This makes it possible to check (manually) at the call boundary that the cache has the necessary invalidation. In some places, this means we need to retrieve fields from GlobalKnowledge (because they are non-trivial to check for equality, we re-use the machinery). We also fix the invalidation of FullClassCache surfaced by this change. --- .../linker/backend/emitter/ClassEmitter.scala | 277 ++++++++---------- .../linker/backend/emitter/Emitter.scala | 72 ++++- .../backend/emitter/FunctionEmitter.scala | 2 +- .../backend/emitter/GlobalKnowledge.scala | 8 +- .../backend/emitter/KnowledgeGuardian.scala | 6 +- 5 files changed, 179 insertions(+), 186 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala index a12406a32d..f7d0a54365 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala @@ -43,16 +43,15 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { import nameGen._ import varGen._ - def buildClass(tree: LinkedClass, useESClass: Boolean, ctor: js.Tree, + def buildClass(className: ClassName, kind: ClassKind, jsClassCaptures: Option[List[ParamDef]], + hasClassInitializer: Boolean, + superClass: Option[ClassIdent], jsSuperClass: Option[Tree], useESClass: Boolean, ctor: js.Tree, memberDefs: List[js.MethodDef], exportedDefs: List[js.Tree])( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { - - implicit val pos = tree.pos + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - val className = tree.name.name def allES6Defs = { - js.Block(ctor +: (memberDefs ++ exportedDefs))(tree.pos) match { + js.Block(ctor +: (memberDefs ++ exportedDefs)) match { case js.Block(allDefs) => allDefs case js.Skip() => Nil case oneDef => List(oneDef) @@ -64,13 +63,13 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { ctor, assignES5ClassMembers(classVar, memberDefs), js.Block(exportedDefs: _*))) } - if (!tree.kind.isJSClass) { - assert(tree.jsSuperClass.isEmpty, className) + if (!kind.isJSClass) { + assert(jsSuperClass.isEmpty, className) if (useESClass) { - val parentVarWithGlobals = for (parentIdent <- tree.superClass) yield { + val parentVarWithGlobals = for (parentIdent <- superClass) yield { implicit val pos = parentIdent.pos - if (shouldExtendJSError(tree)) untrackedGlobalRef("Error") + if (shouldExtendJSError(className, superClass)) untrackedGlobalRef("Error") else WithGlobals(globalVar("c", parentIdent.name)) } @@ -84,7 +83,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { // Wrap the entire class def in an accessor function import TreeDSL._ - val genStoreJSSuperClass = tree.jsSuperClass.map { jsSuperClass => + val genStoreJSSuperClass = jsSuperClass.map { jsSuperClass => for (rhs <- desugarExpr(jsSuperClass, resultType = AnyType)) yield { js.VarDef(fileLevelVar("superClass").ident, Some(rhs)) } @@ -95,7 +94,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val createClassValueVar = genEmptyMutableLet(classValueIdent) val entireClassDefWithGlobals = if (useESClass) { - genJSSuperCtor(tree).map { jsSuperClass => + genJSSuperCtor(superClass, jsSuperClass).map { jsSuperClass => classValueVar := js.ClassDef(Some(classValueIdent), Some(jsSuperClass), allES6Defs) } } else { @@ -105,19 +104,19 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val classDefStatsWithGlobals = for { optStoreJSSuperClass <- WithGlobals.option(genStoreJSSuperClass) entireClassDef <- entireClassDefWithGlobals - createStaticFields <- genCreateStaticFieldsOfJSClass(tree) + createStaticFields <- genCreateStaticFieldsOfJSClass(className) } yield { optStoreJSSuperClass.toList ::: entireClassDef :: createStaticFields } - tree.jsClassCaptures.fold { + jsClassCaptures.fold { for { classDefStats <- classDefStatsWithGlobals body = js.Block( js.If(!classValueVar, { js.Block( classDefStats ::: - genClassInitialization(tree) + genClassInitialization(className, hasClassInitializer) ) }, { js.Skip() @@ -136,7 +135,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { js.ParamDef(ident) } - assert(!hasClassInitializer(tree), + assert(!hasClassInitializer, s"Found a class initializer in the non-top-level class $className") classDefStatsWithGlobals.flatMap { classDefStats => @@ -171,30 +170,28 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } /** Generates the JS constructor for a class. */ - def genConstructor(tree: LinkedClass, useESClass: Boolean, - initToInline: Option[MethodDef])( + def genConstructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent], + jsSuperClass: Option[Tree], useESClass: Boolean, + initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - assert(tree.kind.isAnyNonNativeClass) - assert(tree.superClass.isDefined || tree.name.name == ObjectClass, - s"Class ${tree.name.name} is missing a parent class") + assert(kind.isAnyNonNativeClass) + assert(superClass.isDefined || className == ObjectClass, + s"Class $className is missing a parent class") if (useESClass) - genES6Constructor(tree, initToInline) + genES6Constructor(className, kind, superClass, initToInline, jsConstructorDef) else - genES5Constructor(tree, initToInline) + genES5Constructor(className, kind, superClass, jsSuperClass, initToInline, jsConstructorDef) } /** Generates the JS constructor for a class, ES5 style. */ - private def genES5Constructor(tree: LinkedClass, - initToInline: Option[MethodDef])( + private def genES5Constructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent], + jsSuperClass: Option[Tree], initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { import TreeDSL._ - implicit val pos = tree.pos - - val className = tree.name.name def chainPrototypeWithLocalCtor(ctorVar: js.Tree, superCtor: js.Tree): js.Tree = { val dummyCtor = fileLevelVar("hh", genName(className)) @@ -207,14 +204,14 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { ) } - if (!tree.kind.isJSClass) { + if (!kind.isJSClass) { val ctorVar = globalVar("c", className) - val chainProtoWithGlobals = tree.superClass match { + val chainProtoWithGlobals = superClass match { case None => WithGlobals(js.Skip()) - case Some(_) if shouldExtendJSError(tree) => + case Some(_) if shouldExtendJSError(className, superClass) => untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(ctorVar, _)) case Some(parentIdent) => @@ -222,7 +219,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } for { - ctorFun <- genJSConstructorFun(tree, initToInline, forESClass = false) + ctorFun <- genJSConstructorFun(className, superClass, initToInline, forESClass = false) realCtorDef <- globalFunctionDef("c", className, ctorFun.args, ctorFun.restParam, ctorFun.body) inheritableCtorDef <- @@ -244,8 +241,8 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } else { for { - ctorFun <- genConstructorFunForJSClass(tree) - superCtor <- genJSSuperCtor(tree) + ctorFun <- genConstructorFunForJSClass(className, jsConstructorDef) + superCtor <- genJSSuperCtor(superClass, jsSuperClass) } yield { val ctorVar = fileLevelVar("b", genName(className)) @@ -260,20 +257,18 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } /** Generates the JS constructor for a class, ES6 style. */ - private def genES6Constructor(tree: LinkedClass, - initToInline: Option[MethodDef])( + private def genES6Constructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent], + initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { - implicit val pos = tree.pos - - if (tree.kind.isJSClass) { - for (fun <- genConstructorFunForJSClass(tree)) yield { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { + if (kind.isJSClass) { + for (fun <- genConstructorFunForJSClass(className, jsConstructorDef)) yield { js.MethodDef(static = false, js.Ident("constructor"), fun.args, fun.restParam, fun.body) } } else { val jsConstructorFunWithGlobals = - genJSConstructorFun(tree, initToInline, forESClass = true) + genJSConstructorFun(className, superClass, initToInline, forESClass = true) for (jsConstructorFun <- jsConstructorFunWithGlobals) yield { val js.Function(_, args, restParam, body) = jsConstructorFun @@ -292,16 +287,13 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def genJSSuperCtor(tree: LinkedClass)( + private def genJSSuperCtor(superClass: Option[ClassIdent], jsSuperClass: Option[Tree])( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - assert(tree.kind.isJSClass) - - if (tree.jsSuperClass.isDefined) { + if (jsSuperClass.isDefined) { WithGlobals(fileLevelVar("superClass")) } else { - genJSClassConstructor(tree.superClass.get.name, - keepOnlyDangerousVarNames = true) + genJSClassConstructor(superClass.get.name, keepOnlyDangerousVarNames = true) } } @@ -331,22 +323,20 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { * @param initToInline * The `init` method to inline in the JS constructor, if any. */ - private def genJSConstructorFun(tree: LinkedClass, - initToInline: Option[MethodDef], forESClass: Boolean)( + private def genJSConstructorFun(className: ClassName, + superClass: Option[ClassIdent], initToInline: Option[MethodDef], forESClass: Boolean)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Function] = { - - implicit val pos = tree.pos - + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Function] = { val superCtorCallAndFieldDefs = if (forESClass) { - val fieldDefs = genFieldDefsOfScalaClass(tree.className, tree.fields) - if (tree.superClass.isEmpty) + val fieldDefs = genFieldDefsOfScalaClass(className, + globalKnowledge.getFieldDefs(className)) + if (superClass.isEmpty) fieldDefs else js.Apply(js.Super(), Nil) :: fieldDefs } else { val allFields = - globalKnowledge.getAllScalaClassFieldDefs(tree.className) + globalKnowledge.getAllScalaClassFieldDefs(className) allFields.flatMap { classAndFields => genFieldDefsOfScalaClass(classAndFields._1, classAndFields._2) } @@ -363,7 +353,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } assert(initMethodDef.resultType == NoType, s"Found a constructor with type ${initMethodDef.resultType} at $pos") - desugarToFunction(tree.className, initMethodDef.args, initMethodBody, + desugarToFunction(className, initMethodDef.args, initMethodBody, resultType = NoType) } @@ -375,19 +365,17 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def genConstructorFunForJSClass(tree: LinkedClass)( + private def genConstructorFunForJSClass(className: ClassName, + jsConstructorDef: Option[JSConstructorDef])( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Function] = { - implicit val pos = tree.pos - - require(tree.kind.isJSClass) + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Function] = { - val JSConstructorDef(_, params, restParam, body) = tree.jsConstructorDef.getOrElse { + val JSConstructorDef(_, params, restParam, body) = jsConstructorDef.getOrElse { throw new IllegalArgumentException( - s"${tree.className} does not have an exported constructor") + s"$className does not have an exported constructor") } - desugarToFunction(tree.className, params, restParam, body) + desugarToFunction(className, params, restParam, body) } /** Generates the creation of fields for a Scala class. */ @@ -407,16 +395,16 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } /** Generates the creation of the static fields for a Scala class. */ - def genCreateStaticFieldsOfScalaClass(tree: LinkedClass)( + def genCreateStaticFieldsOfScalaClass(className: ClassName)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[List[js.Tree]] = { val defs = for { - field @ FieldDef(flags, FieldIdent(name), origName, ftpe) <- tree.fields + field @ FieldDef(flags, FieldIdent(name), origName, ftpe) <- globalKnowledge.getFieldDefs(className) if flags.namespace.isStatic } yield { implicit val pos = field.pos - val varScope = (tree.className, name) + val varScope = (className, name) val value = genZeroOf(ftpe) if (flags.isMutable) @@ -431,11 +419,11 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { /** Generates the creation of the private JS field defs for a JavaScript * class. */ - def genCreatePrivateJSFieldDefsOfJSClass(tree: LinkedClass)( + def genCreatePrivateJSFieldDefsOfJSClass(className: ClassName)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[List[js.Tree]] = { val defs = for { - field @ FieldDef(flags, FieldIdent(name), origName, _) <- tree.fields + field @ FieldDef(flags, FieldIdent(name), origName, _) <- globalKnowledge.getFieldDefs(className) if !flags.namespace.isStatic } yield { implicit val pos = field.pos @@ -449,7 +437,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } symbolValueWithGlobals.flatMap { symbolValue => - globalVarDef("r", (tree.className, name), symbolValue, origName.orElse(name)) + globalVarDef("r", (className, name), symbolValue, origName.orElse(name)) } } @@ -457,12 +445,11 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } /** Generates the creation of the static fields for a JavaScript class. */ - private def genCreateStaticFieldsOfJSClass(tree: LinkedClass)( + private def genCreateStaticFieldsOfJSClass(className: ClassName)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[List[js.Tree]] = { - val className = tree.className val statsWithGlobals = for { - field <- tree.fields + field <- globalKnowledge.getFieldDefs(className) if field.flags.namespace.isStatic } yield { implicit val pos = field.pos @@ -489,22 +476,20 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } /** Generates the static initializer invocation of a class. */ - def genStaticInitialization(tree: LinkedClass)( + def genStaticInitialization(className: ClassName)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): List[js.Tree] = { - implicit val pos = tree.pos - val field = globalVar("sct", (tree.className, StaticInitializerName), + globalKnowledge: GlobalKnowledge, pos: Position): List[js.Tree] = { + val field = globalVar("sct", (className, StaticInitializerName), StaticInitializerOriginalName) js.Apply(field, Nil) :: Nil } /** Generates the class initializer invocation of a class. */ - private def genClassInitialization(tree: LinkedClass)( + private def genClassInitialization(className: ClassName, hasClassInitializer: Boolean)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): List[js.Tree] = { - implicit val pos = tree.pos - if (hasClassInitializer(tree)) { - val field = globalVar("sct", (tree.className, ClassInitializerName), + globalKnowledge: GlobalKnowledge, pos: Position): List[js.Tree] = { + if (hasClassInitializer) { + val field = globalVar("sct", (className, ClassInitializerName), ClassInitializerOriginalName) js.Apply(field, Nil) :: Nil } else { @@ -512,13 +497,6 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def hasClassInitializer(tree: LinkedClass): Boolean = { - tree.methods.exists { m => - m.flags.namespace == MemberNamespace.StaticConstructor && - m.methodName.isClassInitializer - } - } - def genMemberMethod(className: ClassName, method: MethodDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.MethodDef] = { @@ -583,7 +561,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } /** Generates a JS method. */ - private def genJSMethod(tree: LinkedClass, useESClass: Boolean, + private def genJSMethod(className: ClassName, kind: ClassKind, useESClass: Boolean, method: JSMethodDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { @@ -593,45 +571,45 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { assert(!namespace.isPrivate && !namespace.isConstructor) for { - methodFun <- desugarToFunction(tree.className, method.args, method.restParam, method.body, AnyType) + methodFun <- desugarToFunction(className, method.args, method.restParam, method.body, AnyType) propName <- genMemberNameTree(method.name) } yield { if (useESClass) { js.MethodDef(static = namespace.isStatic, propName, methodFun.args, methodFun.restParam, methodFun.body) } else { - val targetObject = exportTargetES5(tree, namespace) + val targetObject = exportTargetES5(className, kind, namespace) js.Assign(genPropSelect(targetObject, propName), methodFun) } } } /** Generates a property. */ - private def genJSProperty(tree: LinkedClass, useESClass: Boolean, + private def genJSProperty(className: ClassName, kind: ClassKind, useESClass: Boolean, property: JSPropertyDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { if (useESClass) - genJSPropertyES6(tree.className, property) + genJSPropertyES6(className, property) else - genJSPropertyES5(tree, property) + genJSPropertyES5(className, kind, property) } - private def genJSPropertyES5(tree: LinkedClass, property: JSPropertyDef)( + private def genJSPropertyES5(className: ClassName, kind: ClassKind, property: JSPropertyDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { implicit val pos = property.pos - val targetObject = exportTargetES5(tree, property.flags.namespace) + val targetObject = exportTargetES5(className, kind, property.flags.namespace) // optional getter definition val optGetterWithGlobals = property.getterBody map { body => - desugarToFunction(tree.className, Nil, body, resultType = AnyType) + desugarToFunction(className, Nil, body, resultType = AnyType) } // optional setter definition val optSetterWithGlobals = property.setterArgAndBody map { case (arg, body) => - desugarToFunction(tree.className, arg :: Nil, body, resultType = NoType) + desugarToFunction(className, arg :: Nil, body, resultType = NoType) } for { @@ -680,14 +658,14 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def exportTargetES5(tree: LinkedClass, namespace: MemberNamespace)( + private def exportTargetES5(className: ClassName, kind: ClassKind, namespace: MemberNamespace)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge, pos: Position): js.Tree = { import TreeDSL._ val classVarRef = - if (tree.kind.isJSClass) fileLevelVar("b", genName(tree.className)) - else globalVar("c", tree.className) + if (kind.isJSClass) fileLevelVar("b", genName(className)) + else globalVar("c", className) if (namespace.isStatic) classVarRef else classVarRef.prototype @@ -734,34 +712,29 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { globalKnowledge.isAncestorOfHijackedClass(tree.className)) } - def genInstanceTests(tree: LinkedClass)( + def genInstanceTests(className: ClassName, kind: ClassKind)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { for { - single <- genSingleInstanceTests(tree) - array <- genArrayInstanceTests(tree) + single <- genSingleInstanceTests(className, kind) + array <- genArrayInstanceTests(className) } yield { - js.Block(single ::: array)(tree.pos) + js.Block(single ::: array) } } - private def genSingleInstanceTests(tree: LinkedClass)( + private def genSingleInstanceTests(className: ClassName, kind: ClassKind)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[List[js.Tree]] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[List[js.Tree]] = { import TreeDSL._ - implicit val pos = tree.pos - - val className = tree.name.name - // Instance tests for java.lang.Object are generated by the CoreJSLib assert(className != ObjectClass, "cannot call genSingleInstanceTests for java.lang.Object") - val isHijackedClass = - tree.kind == ClassKind.HijackedClass + val isHijackedClass = kind == ClassKind.HijackedClass - if (tree.kind.isClass || tree.kind == ClassKind.Interface || isHijackedClass) { + if (kind.isClass || kind == ClassKind.Interface || isHijackedClass) { val displayName = className.nameString val objParam = js.ParamDef(js.Ident("obj")) @@ -770,7 +743,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val isExpression = if (isHijackedClass) { genIsInstanceOfHijackedClass(obj, className) } else { - val baseTest = if (tree.kind.isClass) { + val baseTest = if (kind.isClass) { genIsInstanceOfClass(obj, className) } else { !(!( @@ -793,7 +766,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } val needIsFunction = !isHijackedClass && { - !tree.kind.isClass || + !kind.isClass || globalKnowledge.isAncestorOfHijackedClass(className) } @@ -831,14 +804,11 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def genArrayInstanceTests(tree: LinkedClass)( + private def genArrayInstanceTests(className: ClassName)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[List[js.Tree]] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[List[js.Tree]] = { import TreeDSL._ - implicit val pos = tree.pos - - val className = tree.name.name val displayName = className.nameString // Array instance tests for java.lang.Object are generated by the CoreJSLib @@ -898,16 +868,13 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { ancestors DOT genName(className) } - def genTypeData(tree: LinkedClass)( + def genTypeData(className: ClassName, kind: ClassKind, + superClass: Option[ClassIdent], ancestors: List[ClassName], + jsNativeLoadSpec: Option[JSNativeLoadSpec])( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { import TreeDSL._ - implicit val pos = tree.pos - - val className = tree.name.name - val kind = tree.kind - val isObjectClass = className == ObjectClass val isJSType = @@ -918,7 +885,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { else js.Undefined() val parentData = if (globalKnowledge.isParentDataAccessed) { - tree.superClass.fold[js.Tree] { + superClass.fold[js.Tree] { if (isObjectClass) js.Null() else js.Undefined() } { parent => @@ -929,7 +896,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } val ancestorsRecord = js.ObjectConstr( - tree.ancestors.map(ancestor => (js.Ident(genName(ancestor)), js.IntLiteral(1)))) + ancestors.map(ancestor => (js.Ident(genName(ancestor)), js.IntLiteral(1)))) val isInstanceFunWithGlobals: WithGlobals[js.Tree] = { if (globalKnowledge.isAncestorOfHijackedClass(className)) { @@ -955,7 +922,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { WithGlobals(globalVar("noIsInstance", CoreVar)) } else { for { - jsCtor <- genJSClassConstructor(className, tree.jsNativeLoadSpec, + jsCtor <- genJSClassConstructor(className, jsNativeLoadSpec, keepOnlyDangerousVarNames = true) } yield { genArrowFunction(List(js.ParamDef(js.Ident("x"))), None, js.Return { @@ -974,7 +941,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { js.ObjectConstr(List(js.Ident(genName(className)) -> js.IntLiteral(0))), js.BooleanLiteral(kind == ClassKind.Interface), js.StringLiteral(RuntimeClassNameMapperImpl.map( - semantics.runtimeClassNameMapper, tree.fullName)), + semantics.runtimeClassNameMapper, className.nameString)), ancestorsRecord, isJSTypeParam, parentData, @@ -991,30 +958,22 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - def genSetTypeData(tree: LinkedClass)( + def genSetTypeData(className: ClassName)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): js.Tree = { + globalKnowledge: GlobalKnowledge, pos: Position): js.Tree = { import TreeDSL._ - implicit val pos = tree.pos - - assert(tree.kind.isClass) - - globalVar("c", tree.name.name).prototype DOT "$classData" := - globalVar("d", tree.name.name) + globalVar("c", className).prototype DOT "$classData" := globalVar("d", className) } - def genModuleAccessor(tree: LinkedClass)( + def genModuleAccessor(className: ClassName, kind: ClassKind)( implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { import TreeDSL._ - implicit val pos = tree.pos - - val className = tree.name.name val tpe = ClassType(className) - require(tree.kind.hasModuleAccessor, + require(kind.hasModuleAccessor, s"genModuleAccessor called with non-module class: $className") val moduleInstance = fileLevelVarIdent("n", genName(className)) @@ -1026,7 +985,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val assignModule = { moduleInstanceVar := { - if (tree.kind == ClassKind.JSModuleClass) { + if (kind == ClassKind.JSModuleClass) { js.New( genNonNativeJSClassConstructor(className), Nil) @@ -1066,12 +1025,12 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { createAccessor.map(js.Block(createModuleInstanceField, _)) } - def genExportedMember(tree: LinkedClass, useESClass: Boolean, member: JSMethodPropDef)( + def genExportedMember(className: ClassName, kind: ClassKind, useESClass: Boolean, member: JSMethodPropDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { member match { - case m: JSMethodDef => genJSMethod(tree, useESClass, m) - case p: JSPropertyDef => genJSProperty(tree, useESClass, p) + case m: JSMethodDef => genJSMethod(className, kind, useESClass, m) + case p: JSPropertyDef => genJSProperty(className, kind, useESClass, p) } } @@ -1219,8 +1178,6 @@ private[emitter] object ClassEmitter { private val ClassInitializerOriginalName: OriginalName = OriginalName("") - def shouldExtendJSError(linkedClass: LinkedClass): Boolean = { - linkedClass.name.name == ThrowableClass && - linkedClass.superClass.exists(_.name == ObjectClass) - } + def shouldExtendJSError(className: ClassName, superClass: Option[ClassIdent]): Boolean = + className == ThrowableClass && superClass.exists(_.name == ObjectClass) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala index 82a84fd72f..50a1fc9383 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala @@ -400,7 +400,7 @@ final class Emitter(config: Emitter.Config) { // Symbols for private JS fields if (kind.isJSClass) { val fieldDefs = classTreeCache.privateJSFields.getOrElseUpdate { - classEmitter.genCreatePrivateJSFieldDefsOfJSClass(linkedClass)( + classEmitter.genCreatePrivateJSFieldDefsOfJSClass(className)( moduleContext, classCache) } main ++= extractWithGlobals(fieldDefs) @@ -472,15 +472,24 @@ final class Emitter(config: Emitter.Config) { } } } + // TODO: This is probably better split into JS / Scala class ctors. ctorCache.getOrElseUpdate(ctorVersion, - classEmitter.genConstructor(linkedClass, useESClass, linkedInlineableInit)(moduleContext, ctorCache)) + classEmitter.genConstructor( + className, // invalidated by overall class cache (part of ancestors) + kind, // invalidated by class verison + linkedClass.superClass, // invalidated by class version + linkedClass.jsSuperClass, // invalidated by class version + useESClass, // invalidated by class version, + linkedInlineableInit, // part of ctor version + linkedClass.jsConstructorDef // part of ctor version + )(moduleContext, ctorCache, linkedClass.pos)) } /* Bridges from Throwable to methods of Object, which are necessary * because Throwable is rewired to extend JavaScript's Error instead of * j.l.Object. */ - val linkedMethodsAndBridges = if (ClassEmitter.shouldExtendJSError(linkedClass)) { + val linkedMethodsAndBridges = if (ClassEmitter.shouldExtendJSError(className, linkedClass.superClass)) { val existingMethods = linkedMethods .withFilter(_.flags.namespace == MemberNamespace.Public) .map(_.methodName) @@ -530,20 +539,42 @@ final class Emitter(config: Emitter.Config) { val memberCache = classCache.getExportedMemberCache(idx) val version = Version.combine(linkedClass.version, member.version) memberCache.getOrElseUpdate(version, - classEmitter.genExportedMember(linkedClass, useESClass, member)(moduleContext, memberCache)) + classEmitter.genExportedMember( + className, // invalidated by overall class cache + kind, // invalidated by class verison + useESClass, // invalidated by class version (combined) + member // invalidated by version (combined) + )(moduleContext, memberCache)) + } + + val hasClassInitializer: Boolean = { + linkedClass.methods.exists { m => + m.flags.namespace == MemberNamespace.StaticConstructor && + m.methodName.isClassInitializer + } } val fullClass = { val fullClassCache = classCache.getFullClassCache() - fullClassCache.getOrElseUpdate(useESClass, ctorWithGlobals, + fullClassCache.getOrElseUpdate(linkedClass.version, ctorWithGlobals, memberMethodsWithGlobals, exportedMembersWithGlobals, { for { ctor <- ctorWithGlobals memberMethods <- WithGlobals.list(memberMethodsWithGlobals) exportedMembers <- WithGlobals.list(exportedMembersWithGlobals) - clazz <- classEmitter.buildClass(linkedClass, useESClass, ctor, - memberMethods, exportedMembers)(moduleContext, fullClassCache) + clazz <- classEmitter.buildClass( + className, // invalidated by overall class cache (part of ancestors) + linkedClass.kind, // invalidated by class version + linkedClass.jsClassCaptures, // invalidated by class version + hasClassInitializer, // invalidated by class version (optimizer cannot remove it) + linkedClass.superClass, // invalidated by class version + linkedClass.jsSuperClass, // invalidated by class version + useESClass, // invalidated by class version (depends on kind, config and ancestry only) + ctor, // invalidated directly + memberMethods, // invalidated directly + exportedMembers // invalidated directly + )(moduleContext, fullClassCache, linkedClass.pos) // pos invalidated by class version } yield { clazz } @@ -569,23 +600,29 @@ final class Emitter(config: Emitter.Config) { if (classEmitter.needInstanceTests(linkedClass)(classCache)) { addToMain(classTreeCache.instanceTests.getOrElseUpdate( - classEmitter.genInstanceTests(linkedClass)(moduleContext, classCache))) + classEmitter.genInstanceTests(className, kind)(moduleContext, classCache, linkedClass.pos))) } if (linkedClass.hasRuntimeTypeInfo) { addToMain(classTreeCache.typeData.getOrElseUpdate( - classEmitter.genTypeData(linkedClass)(moduleContext, classCache))) + classEmitter.genTypeData( + className, // invalidated by overall class cache (part of ancestors) + kind, // invalidated by class version + linkedClass.superClass, // invalidated by class version + linkedClass.ancestors, // invalidated by overall class cache (identity) + linkedClass.jsNativeLoadSpec // invalidated by class version + )(moduleContext, classCache, linkedClass.pos))) } if (linkedClass.hasInstances && kind.isClass && linkedClass.hasRuntimeTypeInfo) { main += classTreeCache.setTypeData.getOrElseUpdate( - classEmitter.genSetTypeData(linkedClass)(moduleContext, classCache)) + classEmitter.genSetTypeData(className)(moduleContext, classCache, linkedClass.pos)) } } if (linkedClass.kind.hasModuleAccessor) addToMain(classTreeCache.moduleAccessor.getOrElseUpdate( - classEmitter.genModuleAccessor(linkedClass)(moduleContext, classCache))) + classEmitter.genModuleAccessor(className, kind)(moduleContext, classCache, linkedClass.pos))) // Static fields @@ -593,14 +630,14 @@ final class Emitter(config: Emitter.Config) { Nil } else { extractWithGlobals(classTreeCache.staticFields.getOrElseUpdate( - classEmitter.genCreateStaticFieldsOfScalaClass(linkedClass)(moduleContext, classCache))) + classEmitter.genCreateStaticFieldsOfScalaClass(className)(moduleContext, classCache))) } // Static initialization val staticInitialization = if (classEmitter.needStaticInitialization(linkedClass)) { classTreeCache.staticInitialization.getOrElseUpdate( - classEmitter.genStaticInitialization(linkedClass)(moduleContext, classCache)) + classEmitter.genStaticInitialization(className)(moduleContext, classCache, linkedClass.pos)) } else { Nil } @@ -853,7 +890,7 @@ final class Emitter(config: Emitter.Config) { private class FullClassCache extends knowledgeGuardian.KnowledgeAccessor { private[this] var _tree: WithGlobals[js.Tree] = null - private[this] var _lastUseESClass: Boolean = false + private[this] var _lastVersion: Version = Version.Unversioned private[this] var _lastCtor: WithGlobals[js.Tree] = null private[this] var _lastMemberMethods: List[WithGlobals[js.MethodDef]] = null private[this] var _lastExportedMembers: List[WithGlobals[js.Tree]] = null @@ -862,6 +899,7 @@ final class Emitter(config: Emitter.Config) { override def invalidate(): Unit = { super.invalidate() _tree = null + _lastVersion = Version.Unversioned _lastCtor = null _lastMemberMethods = null _lastExportedMembers = null @@ -869,7 +907,7 @@ final class Emitter(config: Emitter.Config) { def startRun(): Unit = _cacheUsed = false - def getOrElseUpdate(useESClass: Boolean, ctor: WithGlobals[js.Tree], + def getOrElseUpdate(version: Version, ctor: WithGlobals[js.Tree], memberMethods: List[WithGlobals[js.MethodDef]], exportedMembers: List[WithGlobals[js.Tree]], compute: => WithGlobals[js.Tree]): WithGlobals[js.Tree] = { @@ -881,10 +919,12 @@ final class Emitter(config: Emitter.Config) { } } - if (_tree == null || (_lastCtor ne ctor) || !allSame(_lastMemberMethods, memberMethods) || + if (_tree == null || !version.sameVersion(_lastVersion) || (_lastCtor ne ctor) || + !allSame(_lastMemberMethods, memberMethods) || !allSame(_lastExportedMembers, exportedMembers)) { invalidate() _tree = compute + _lastVersion = version _lastCtor = ctor _lastMemberMethods = memberMethods _lastExportedMembers = exportedMembers diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala index 145eb89285..d0cd14d62c 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala @@ -794,7 +794,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } val enclosingClassFieldDefs = - globalKnowledge.getJSClassFieldDefs(enclosingClassName) + globalKnowledge.getFieldDefs(enclosingClassName) val fieldDefs = for { field <- enclosingClassFieldDefs diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala index a8e211c986..1122c4b935 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala @@ -78,12 +78,8 @@ private[emitter] trait GlobalKnowledge { */ def getSuperClassOfJSClass(className: ClassName): ClassName - /** The `FieldDef`s of a non-native JS class. - * - * It is invalid to call this method with a class that is not a non-native - * JS class. - */ - def getJSClassFieldDefs(className: ClassName): List[AnyFieldDef] + /** The `FieldDef`s of a class. */ + def getFieldDefs(className: ClassName): List[AnyFieldDef] /** The global variables that mirror a given static field. */ def getStaticFieldMirrors(className: ClassName, field: FieldName): List[String] diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala index 21ba3c0640..cd3bd4a687 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala @@ -202,8 +202,8 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { def getSuperClassOfJSClass(className: ClassName): ClassName = classes(className).askJSSuperClass(this) - def getJSClassFieldDefs(className: ClassName): List[AnyFieldDef] = - classes(className).askJSClassFieldDefs(this) + def getFieldDefs(className: ClassName): List[AnyFieldDef] = + classes(className).askFieldDefs(this) def getStaticFieldMirrors(className: ClassName, field: FieldName): List[String] = classes(className).askStaticFieldMirrors(this, field) @@ -449,7 +449,7 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { superClass } - def askJSClassFieldDefs(invalidatable: Invalidatable): List[AnyFieldDef] = { + def askFieldDefs(invalidatable: Invalidatable): List[AnyFieldDef] = { invalidatable.registeredTo(this) fieldDefsAskers += invalidatable fieldDefs From 7c6659a9a3f4ac16a89195641f5ab7dc053e48f4 Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Sat, 13 May 2023 14:13:52 +0200 Subject: [PATCH 09/27] Refactor constructor generation We split on the class kind (Scala / JS) first and then on the output class style (ES5 / ES6). --- .../linker/backend/emitter/ClassEmitter.scala | 123 ++++++++---------- .../linker/backend/emitter/Emitter.scala | 51 +++++--- 2 files changed, 83 insertions(+), 91 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala index f7d0a54365..02801ae782 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala @@ -169,42 +169,36 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - /** Generates the JS constructor for a class. */ - def genConstructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent], - jsSuperClass: Option[Tree], useESClass: Boolean, - initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])( + /** Generates the JS constructor for a Scala class. */ + def genScalaClassConstructor(className: ClassName, superClass: Option[ClassIdent], + useESClass: Boolean, initToInline: Option[MethodDef])( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - assert(kind.isAnyNonNativeClass) assert(superClass.isDefined || className == ObjectClass, s"Class $className is missing a parent class") - if (useESClass) - genES6Constructor(className, kind, superClass, initToInline, jsConstructorDef) - else - genES5Constructor(className, kind, superClass, jsSuperClass, initToInline, jsConstructorDef) - } + val jsConstructorFunWithGlobals = + genJSConstructorFun(className, superClass, initToInline, useESClass) - /** Generates the JS constructor for a class, ES5 style. */ - private def genES5Constructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent], - jsSuperClass: Option[Tree], initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])( - implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - import TreeDSL._ + if (useESClass) { + for (jsConstructorFun <- jsConstructorFunWithGlobals) yield { + val js.Function(_, args, restParam, body) = jsConstructorFun - def chainPrototypeWithLocalCtor(ctorVar: js.Tree, superCtor: js.Tree): js.Tree = { - val dummyCtor = fileLevelVar("hh", genName(className)) + def isTrivialCtorBody: Boolean = body match { + case js.Skip() => true + case js.Apply(js.Super(), Nil) => true + case _ => false + } - js.Block( - js.DocComment("@constructor"), - genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip())), - dummyCtor.prototype := superCtor.prototype, - ctorVar.prototype := js.New(dummyCtor, Nil) - ) - } + if (args.isEmpty && isTrivialCtorBody) + js.Skip() + else + js.MethodDef(static = false, js.Ident("constructor"), args, restParam, body) + } + } else { + import TreeDSL._ - if (!kind.isJSClass) { val ctorVar = globalVar("c", className) val chainProtoWithGlobals = superClass match { @@ -212,14 +206,14 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { WithGlobals(js.Skip()) case Some(_) if shouldExtendJSError(className, superClass) => - untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(ctorVar, _)) + untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(className, ctorVar, _)) case Some(parentIdent) => WithGlobals(ctorVar.prototype := js.New(globalVar("h", parentIdent.name), Nil)) } for { - ctorFun <- genJSConstructorFun(className, superClass, initToInline, forESClass = false) + ctorFun <- jsConstructorFunWithGlobals realCtorDef <- globalFunctionDef("c", className, ctorFun.args, ctorFun.restParam, ctorFun.body) inheritableCtorDef <- @@ -239,50 +233,38 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { globalVar("h", className).prototype := ctorVar.prototype ) } - } else { - for { - ctorFun <- genConstructorFunForJSClass(className, jsConstructorDef) - superCtor <- genJSSuperCtor(superClass, jsSuperClass) - } yield { - val ctorVar = fileLevelVar("b", genName(className)) - - js.Block( - js.DocComment("@constructor"), - ctorVar := ctorFun, - chainPrototypeWithLocalCtor(ctorVar, superCtor), - genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar - ) - } } } - /** Generates the JS constructor for a class, ES6 style. */ - private def genES6Constructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent], - initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])( + /** Generates the JS constructor for a JS class. */ + def genJSConstructor(className: ClassName, superClass: Option[ClassIdent], + jsSuperClass: Option[Tree], useESClass: Boolean, jsConstructorDef: JSConstructorDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - if (kind.isJSClass) { - for (fun <- genConstructorFunForJSClass(className, jsConstructorDef)) yield { + + val JSConstructorDef(_, params, restParam, body) = jsConstructorDef + val ctorFunWithGlobals = desugarToFunction(className, params, restParam, body) + + if (useESClass) { + for (fun <- ctorFunWithGlobals) yield { js.MethodDef(static = false, js.Ident("constructor"), fun.args, fun.restParam, fun.body) } } else { - val jsConstructorFunWithGlobals = - genJSConstructorFun(className, superClass, initToInline, forESClass = true) - - for (jsConstructorFun <- jsConstructorFunWithGlobals) yield { - val js.Function(_, args, restParam, body) = jsConstructorFun + for { + ctorFun <- ctorFunWithGlobals + superCtor <- genJSSuperCtor(superClass, jsSuperClass) + } yield { + import TreeDSL._ - def isTrivialCtorBody: Boolean = body match { - case js.Skip() => true - case js.Apply(js.Super(), Nil) => true - case _ => false - } + val ctorVar = fileLevelVar("b", genName(className)) - if (args.isEmpty && isTrivialCtorBody) - js.Skip() - else - js.MethodDef(static = false, js.Ident("constructor"), args, restParam, body) + js.Block( + js.DocComment("@constructor"), + ctorVar := ctorFun, + chainPrototypeWithLocalCtor(className, ctorVar, superCtor), + genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar + ) } } } @@ -365,17 +347,18 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def genConstructorFunForJSClass(className: ClassName, - jsConstructorDef: Option[JSConstructorDef])( - implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Function] = { + private def chainPrototypeWithLocalCtor(className: ClassName, ctorVar: js.Tree, + superCtor: js.Tree)(implicit pos: Position): js.Tree = { + import TreeDSL._ - val JSConstructorDef(_, params, restParam, body) = jsConstructorDef.getOrElse { - throw new IllegalArgumentException( - s"$className does not have an exported constructor") - } + val dummyCtor = fileLevelVar("hh", genName(className)) - desugarToFunction(className, params, restParam, body) + js.Block( + js.DocComment("@constructor"), + genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip())), + dummyCtor.prototype := superCtor.prototype, + ctorVar.prototype := js.New(dummyCtor, Nil) + ) } /** Generates the creation of fields for a Scala class. */ diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala index 50a1fc9383..f637d4a8e5 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala @@ -460,29 +460,38 @@ final class Emitter(config: Emitter.Config) { * If it is a JS class, it depends on the jsConstructorDef. */ val ctorCache = classCache.getConstructorCache() - val ctorVersion = { - if (linkedClass.kind.isJSClass) { - assert(linkedInlineableInit.isEmpty) - Version.combine(linkedClass.version, linkedClass.jsConstructorDef.get.version) - } else { - linkedInlineableInit.fold { - Version.combine(linkedClass.version) - } { linkedInit => - Version.combine(linkedClass.version, linkedInit.version) - } + + if (linkedClass.kind.isJSClass) { + assert(linkedInlineableInit.isEmpty) + + val jsConstructorDef = linkedClass.jsConstructorDef.getOrElse { + throw new IllegalArgumentException(s"$className does not have an exported constructor") + } + + val ctorVersion = Version.combine(linkedClass.version, jsConstructorDef.version) + ctorCache.getOrElseUpdate(ctorVersion, + classEmitter.genJSConstructor( + className, // invalidated by overall class cache (part of ancestors) + linkedClass.superClass, // invalidated by class version + linkedClass.jsSuperClass, // invalidated by class version + useESClass, // invalidated by class version + jsConstructorDef // part of ctor version + )(moduleContext, ctorCache, linkedClass.pos)) + } else { + val ctorVersion = linkedInlineableInit.fold { + Version.combine(linkedClass.version) + } { linkedInit => + Version.combine(linkedClass.version, linkedInit.version) } + + ctorCache.getOrElseUpdate(ctorVersion, + classEmitter.genScalaClassConstructor( + className, // invalidated by overall class cache (part of ancestors) + linkedClass.superClass, // invalidated by class version + useESClass, // invalidated by class version, + linkedInlineableInit // part of ctor version + )(moduleContext, ctorCache, linkedClass.pos)) } - // TODO: This is probably better split into JS / Scala class ctors. - ctorCache.getOrElseUpdate(ctorVersion, - classEmitter.genConstructor( - className, // invalidated by overall class cache (part of ancestors) - kind, // invalidated by class verison - linkedClass.superClass, // invalidated by class version - linkedClass.jsSuperClass, // invalidated by class version - useESClass, // invalidated by class version, - linkedInlineableInit, // part of ctor version - linkedClass.jsConstructorDef // part of ctor version - )(moduleContext, ctorCache, linkedClass.pos)) } /* Bridges from Throwable to methods of Object, which are necessary From 9c31dcea2c0c08579c06e2ebb2c4d4b9d1c25283 Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Thu, 18 May 2023 10:02:21 +0200 Subject: [PATCH 10/27] Deprecate broken SymbolRequirement.optional Optional requirements would be much harder to get to work in an incremental analysis scenario. Luckily, the implementation was always broken (observe how Nodes.Optional is never instantiated). Therefore, we simply deprecate the broken factory and remove the unreachable implementation. --- .../scalajs/linker/analyzer/Analyzer.scala | 44 +++++-------------- .../linker/standard/SymbolRequirement.scala | 11 +---- project/BinaryIncompatibilities.scala | 5 +++ 3 files changed, 18 insertions(+), 42 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index e16f1a40be..8b2b4adbd6 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -210,42 +210,26 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } } - private def reachSymbolRequirement(requirement: SymbolRequirement, - optional: Boolean = false): Unit = { + private def reachSymbolRequirement(requirement: SymbolRequirement): Unit = { /* We use j.l.Object as representation of the core infrastructure. * As such, everything depends on j.l.Object and j.l.Object depends on all * symbol requirements. */ - def withClass(className: ClassName)(onSuccess: ClassInfo => Unit)( - implicit from: From): Unit = { - lookupClass(className, ignoreMissing = optional)(onSuccess) - } - - def withMethod(className: ClassName, methodName: MethodName)( - onSuccess: ClassInfo => Unit)( - implicit from: From): Unit = { - withClass(className) { clazz => - val doReach = !optional || clazz.tryLookupMethod(methodName).isDefined - if (doReach) - onSuccess(clazz) - } - } - import SymbolRequirement.Nodes._ requirement match { case AccessModule(origin, moduleName) => implicit val from = FromCore(origin) - withClass(moduleName) { clazz => + lookupClass(moduleName) { clazz => objectClassInfo.staticDependencies += clazz.className clazz.accessModule() } case InstantiateClass(origin, className, constructor) => implicit val from = FromCore(origin) - withMethod(className, constructor) { clazz => + lookupClass(className) { clazz => objectClassInfo.staticDependencies += clazz.className clazz.instantiated() clazz.callMethodStatically(MemberNamespace.Constructor, constructor) @@ -253,21 +237,21 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, case InstanceTests(origin, className) => implicit val from = FromCore(origin) - withClass(className){ clazz => + lookupClass(className){ clazz => objectClassInfo.staticDependencies += clazz.className clazz.useInstanceTests() } case ClassData(origin, className) => implicit val from = FromCore(origin) - withClass(className) { clazz => + lookupClass(className) { clazz => objectClassInfo.staticDependencies += clazz.className clazz.accessData() } case CallMethod(origin, className, methodName, statically) => implicit val from = FromCore(origin) - withMethod(className, methodName) { clazz => + lookupClass(className) { clazz => if (statically) { objectClassInfo.staticDependencies += clazz.className clazz.callMethodStatically(MemberNamespace.Public, methodName) @@ -278,17 +262,14 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, case CallStaticMethod(origin, className, methodName) => implicit val from = FromCore(origin) - withMethod(className, methodName) { clazz => + lookupClass(className) { clazz => objectClassInfo.staticDependencies += clazz.className clazz.callMethodStatically(MemberNamespace.PublicStatic, methodName) } - case Optional(requirement) => - reachSymbolRequirement(requirement, optional = true) - case Multiple(requirements) => for (requirement <- requirements) - reachSymbolRequirement(requirement, optional) + reachSymbolRequirement(requirement) case NoRequirement => // skip } @@ -352,15 +333,12 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } } - private def lookupClass(className: ClassName, - ignoreMissing: Boolean = false)( + private def lookupClass(className: ClassName)( onSuccess: ClassInfo => Unit)(implicit from: From): Unit = { lookupClassForLinking(className, Set.empty) { case info: ClassInfo => - if (!info.nonExistent || !ignoreMissing) { - info.link() - onSuccess(info) - } + info.link() + onSuccess(info) case CycleInfo(cycle, _) => _errors += CycleInInheritanceChain(cycle, fromAnalyzer) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala index e27db97aa1..6838c8341c 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala @@ -79,13 +79,8 @@ object SymbolRequirement { CallStaticMethod(origin, className, methodName) } - def optional(requirement: SymbolRequirement): SymbolRequirement = { - requirement match { - case NoRequirement => NoRequirement - case optional: Optional => optional - case _ => requirement - } - } + @deprecated("broken (not actually optional), do not use", "1.13.2") + def optional(requirement: SymbolRequirement): SymbolRequirement = requirement def multiple(requirements: SymbolRequirement*): SymbolRequirement = multipleInternal(requirements.toList) @@ -123,8 +118,6 @@ object SymbolRequirement { final case class CallStaticMethod(origin: String, className: ClassName, methodName: MethodName) extends SymbolRequirement - final case class Optional(requirement: SymbolRequirement) - extends SymbolRequirement final case class Multiple(requirements: List[SymbolRequirement]) extends SymbolRequirement case object NoRequirement extends SymbolRequirement diff --git a/project/BinaryIncompatibilities.scala b/project/BinaryIncompatibilities.scala index 4713fe6bf8..842ed9f3ed 100644 --- a/project/BinaryIncompatibilities.scala +++ b/project/BinaryIncompatibilities.scala @@ -8,6 +8,11 @@ object BinaryIncompatibilities { ) val Linker = Seq( + // private[linker], not an issue + exclude[MissingClassProblem]( + "org.scalajs.linker.standard.SymbolRequirement$Nodes$Optional"), + exclude[MissingClassProblem]( + "org.scalajs.linker.standard.SymbolRequirement$Nodes$Optional$"), ) val LinkerInterface = Seq( From 9fabf88e566947fbb8b14a802f2d8bb9232bdca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 22 May 2023 09:46:22 +0200 Subject: [PATCH 11/27] Make j.l.Class extend j.io.Serializable and j.l.constant.Constable. --- javalib/src/main/scala/java/lang/Class.scala | 7 ++++++- .../scalajs/testsuite/javalib/lang/ConstableTest.scala | 1 + .../org/scalajs/testsuite/javalib/lang/ClassTest.scala | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/javalib/src/main/scala/java/lang/Class.scala b/javalib/src/main/scala/java/lang/Class.scala index db5cc45ec3..a39bfd3fa7 100644 --- a/javalib/src/main/scala/java/lang/Class.scala +++ b/javalib/src/main/scala/java/lang/Class.scala @@ -12,6 +12,9 @@ package java.lang +import java.lang.constant.Constable +import java.io.Serializable + import scala.scalajs.js @js.native @@ -31,7 +34,9 @@ private trait ScalaJSClassData[A] extends js.Object { def newArrayOfThisClass(dimensions: js.Array[Int]): AnyRef = js.native } -final class Class[A] private (data0: Object) extends Object { +final class Class[A] private (data0: Object) + extends Object with Serializable with Constable { + private[this] val data: ScalaJSClassData[A] = data0.asInstanceOf[ScalaJSClassData[A]] diff --git a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstableTest.scala b/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstableTest.scala index 82876ab933..ec34c35be2 100644 --- a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstableTest.scala +++ b/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstableTest.scala @@ -33,6 +33,7 @@ class ConstableTest { test(true, 1.5f) test(true, 1.4) test(true, "foo") + test(true, classOf[Product]) test(false, null) test(false, ()) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala index ce1980840b..5ec3a41777 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala @@ -47,6 +47,15 @@ class ClassTest { classOf[java.lang.Double] ) + @Test def hierarchy(): Unit = { + def cls: Class[Product] = classOf[Product] + + def serializable: java.io.Serializable = cls + + // prevent DCE with trivial tests + assertSame(cls, serializable) + } + @Test def getPrimitiveTypeName(): Unit = { @noinline def testNoInline(expected: String, cls: Class[_]): Unit = From 7fe4fa79b68fd09a64ccb0a1da57724aa36c73a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 22 May 2023 17:44:33 +0200 Subject: [PATCH 12/27] Fix #4865: Synthesize non-existing constructor in the Constructor namespace. When we link a class extending a non-existing class, we generate a synthetic constructor to avoid follow-up errors. However, we mistakenly put it in the Public namespace, which is invalid. When combined with a reflective call somewhere in the codebase, this triggered the creation of a reflective proxy candidate name for the constructor. That is non-sensical, and was caught by an `IllegalArgumentException` in the `MethodName.reflectiveProxy` constructor. --- .../scalajs/linker/analyzer/Analyzer.scala | 14 ++++++++------ .../org/scalajs/linker/AnalyzerTest.scala | 19 +++++++++++++++++++ .../linker/testutils/TestIRBuilder.scala | 4 ++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index 8b2b4adbd6..1e9aae4cb1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -646,19 +646,19 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } if (candidatesIterator.isEmpty) - createNonExistentMethod(methodName) + createNonExistentPublicMethod(methodName) else candidatesIterator.next() } def lookupMethod(methodName: MethodName): MethodInfo = { tryLookupMethod(methodName).getOrElse { - createNonExistentMethod(methodName) + createNonExistentPublicMethod(methodName) } } - private def createNonExistentMethod(methodName: MethodName): MethodInfo = { - val syntheticData = makeSyntheticMethodInfo(methodName) + private def createNonExistentPublicMethod(methodName: MethodName): MethodInfo = { + val syntheticData = makeSyntheticMethodInfo(methodName, MemberNamespace.Public) val m = new MethodInfo(this, syntheticData) m.nonExistent = true publicMethodInfos += methodName -> m @@ -757,6 +757,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, val syntheticInfo = makeSyntheticMethodInfo( methodName = methodName, + namespace = MemberNamespace.Public, methodsCalledStatically = List( targetOwner.className -> NamespacedMethodName(MemberNamespace.Public, methodName))) val m = new MethodInfo(this, syntheticInfo) @@ -951,6 +952,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, val syntheticInfo = makeSyntheticMethodInfo( methodName = proxyName, + namespace = MemberNamespace.Public, methodsCalled = List(this.className -> targetName)) val m = new MethodInfo(this, syntheticInfo) m.syntheticKind = MethodSyntheticKind.ReflectiveProxy(targetName) @@ -1479,13 +1481,13 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, private def createMissingClassInfo(className: ClassName): Infos.ClassInfo = { new Infos.ClassInfoBuilder(className, ClassKind.Class, superClass = Some(ObjectClass), interfaces = Nil, jsNativeLoadSpec = None) - .addMethod(makeSyntheticMethodInfo(NoArgConstructorName)) + .addMethod(makeSyntheticMethodInfo(NoArgConstructorName, MemberNamespace.Constructor)) .result() } private def makeSyntheticMethodInfo( methodName: MethodName, - namespace: MemberNamespace = MemberNamespace.Public, + namespace: MemberNamespace, methodsCalled: List[(ClassName, MethodName)] = Nil, methodsCalledStatically: List[(ClassName, NamespacedMethodName)] = Nil, instantiatedClasses: List[ClassName] = Nil diff --git a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala index 5074537510..e28017127c 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala @@ -123,6 +123,25 @@ class AnalyzerTest { } } + @Test + def missingClassParentSynthesizingConstructorPlusReflectiveCall_Issue4865(): AsyncResult = await { + val classDefs = Seq( + classDef("A", superClass = Some("B"), + methods = List(trivialCtor("A", parentClassName = "B"))) + ) + + val requirements = { + reqsFactory.instantiateClass("A", NoArgConstructorName) ++ + reqsFactory.callMethod(ObjectClass, MethodName.reflectiveProxy("foo", Nil)) + } + + val analysis = computeAnalysis(classDefs, requirements) + + assertContainsError("MissingClass(B)", analysis) { + case MissingClass(ClsInfo("B"), FromClass(ClsInfo("A"))) => true + } + } + @Test def invalidSuperClass(): AsyncResult = await { val kindsSub = Seq( diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala index e95f27b8c9..8b73664dc8 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala @@ -86,12 +86,12 @@ object TestIRBuilder { ) } - def trivialCtor(enclosingClassName: ClassName): MethodDef = { + def trivialCtor(enclosingClassName: ClassName, parentClassName: ClassName = ObjectClass): MethodDef = { val flags = MemberFlags.empty.withNamespace(MemberNamespace.Constructor) MethodDef(flags, MethodIdent(NoArgConstructorName), NON, Nil, NoType, Some(ApplyStatically(EAF.withConstructor(true), This()(ClassType(enclosingClassName)), - ObjectClass, MethodIdent(NoArgConstructorName), + parentClassName, MethodIdent(NoArgConstructorName), Nil)(NoType)))( EOH, UNV) } From d0ea4604f7ada6475056680177d345492a6a5065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Tue, 16 May 2023 17:28:07 +0200 Subject: [PATCH 13/27] Fix #4850: Handle JS classes that survive only for their data. There were several interleaved issues with JS classes (native or not, module or not) that survived the base linker only for their class data: - Non-native classes lose their `jsConstructorDef` (which is what caused the symptoms in the issue). - Non-native non-module classes generate a `Class.isInstance` test that tries to access a never-declared `$a_C()` accessor. - Module classes lose their module status, triggering the wrong conditions for the `Class.isInstance` tests later on. Some related issues about module accessors were previously handled by patching `ClassKind`s. For module classes whose module accessor was not used, we patched the kind to a corresponding kind without module accessor. This was problematic as well: it caused the third issue above, as well as making the kind unstable over incremental runs (although we tend to assume that the kind is covered by the class version). We solve all these issues at once, by taking a much more principled approach: - We never change the kind of classes. - After base linking, we tolerate `ClassDef`s without constructors even if they are module classes and/or non-native JS classes. - In the emitter, we adjust the generation of module accessors and `Class.isInstance` tests to whether the class effectively has instances. --- .../main/scala/org/scalajs/ir/ClassKind.scala | 1 + .../scalajs/linker/analyzer/InfoLoader.scala | 4 +- .../linker/backend/emitter/ClassEmitter.scala | 8 + .../linker/backend/emitter/Emitter.scala | 3 +- .../linker/checker/ClassDefChecker.scala | 31 ++-- .../scalajs/linker/frontend/BaseLinker.scala | 6 +- .../testsuite/compiler/ReflectionTest.scala | 140 ++++++++++++++++++ 7 files changed, 175 insertions(+), 18 deletions(-) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/ClassKind.scala b/ir/shared/src/main/scala/org/scalajs/ir/ClassKind.scala index 5d6ca2587f..0b46f5e25a 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/ClassKind.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/ClassKind.scala @@ -50,6 +50,7 @@ sealed abstract class ClassKind { case _ => false } + @deprecated("not a meaningful operation", since = "1.13.2") def withoutModuleAccessor: ClassKind = this match { case ModuleClass => Class case JSModuleClass => JSClass diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala index d0b210c47f..a372af291c 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala @@ -88,7 +88,7 @@ private[analyzer] object InfoLoader { case InfoLoader.InitialIRCheck => val errorCount = ClassDefChecker.check(tree, - allowReflectiveProxies = false, allowTransients = false, logger) + postBaseLinker = false, postOptimizer = false, logger) if (errorCount != 0) { throw new LinkingException( s"There were $errorCount ClassDef checking errors.") @@ -96,7 +96,7 @@ private[analyzer] object InfoLoader { case InfoLoader.InternalIRCheck => val errorCount = ClassDefChecker.check(tree, - allowReflectiveProxies = true, allowTransients = true, logger) + postBaseLinker = true, postOptimizer = true, logger) if (errorCount != 0) { throw new LinkingException( s"There were $errorCount ClassDef checking errors after optimizing. " + diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala index 02801ae782..0c87bd53bf 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala @@ -903,6 +903,14 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { */ if (kind != ClassKind.JSClass && kind != ClassKind.NativeJSClass) { WithGlobals(globalVar("noIsInstance", CoreVar)) + } else if (kind == ClassKind.JSClass && !globalKnowledge.hasInstances(className)) { + /* We need to constant-fold the instance test, to avoid emitting + * `x instanceof $a_TheClass()`, because `$a_TheClass` won't be + * declared at all. Otherwise, we'd get a `ReferenceError`. + */ + WithGlobals(genArrowFunction(List(js.ParamDef(js.Ident("x"))), None, js.Return { + js.BooleanLiteral(false) + })) } else { for { jsCtor <- genJSClassConstructor(className, jsNativeLoadSpec, diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala index f637d4a8e5..0c3babc589 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala @@ -629,9 +629,10 @@ final class Emitter(config: Emitter.Config) { } } - if (linkedClass.kind.hasModuleAccessor) + if (linkedClass.kind.hasModuleAccessor && linkedClass.hasInstances) { addToMain(classTreeCache.moduleAccessor.getOrElseUpdate( classEmitter.genModuleAccessor(className, kind)(moduleContext, classCache, linkedClass.pos))) + } // Static fields diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala index 99c170db34..9ce0e7175a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala @@ -27,7 +27,7 @@ import org.scalajs.linker.checker.ErrorReporter._ /** Checker for the validity of the IR. */ private final class ClassDefChecker(classDef: ClassDef, - allowReflectiveProxies: Boolean, allowTransients: Boolean, reporter: ErrorReporter) { + postBaseLinker: Boolean, postOptimizer: Boolean, reporter: ErrorReporter) { import ClassDefChecker._ import reporter.reportError @@ -102,12 +102,23 @@ private final class ClassDefChecker(classDef: ClassDef, //// Additional checks - if (classDef.kind == ClassKind.ModuleClass && - methods(MemberNamespace.Constructor.ordinal).size != 1) - reportError("Module class must have exactly 1 constructor") + /* After the base linker, we may have DCE'ed away the constructors of + * module classes and JS classes that are never instantiated. The classes + * may still exist because their class data are accessed. + */ + if (!postBaseLinker) { + /* Check that we have exactly 1 constructor in a module class. This goes + * together with `checkMethodDef`, which checks that a constructor in a + * module class must be 0-arg. + */ + if (classDef.kind == ClassKind.ModuleClass && + methods(MemberNamespace.Constructor.ordinal).size != 1) { + reportError("Module class must have exactly 1 constructor") + } - if (classDef.kind.isJSClass && classDef.jsConstructor.isEmpty) - reportError("JS classes and module classes must have a constructor") + if (classDef.kind.isJSClass && classDef.jsConstructor.isEmpty) + reportError("JS classes and module classes must have a constructor") + } } private def checkKind()(implicit ctx: ErrorContext): Unit = { @@ -463,7 +474,7 @@ private final class ClassDefChecker(classDef: ClassDef, private def checkMethodNameNamespace(name: MethodName, namespace: MemberNamespace)( implicit ctx: ErrorContext): Unit = { if (name.isReflectiveProxy) { - if (allowReflectiveProxies) { + if (postBaseLinker) { if (namespace != MemberNamespace.Public) reportError("reflective profixes are only allowed in the public namespace") } else { @@ -839,7 +850,7 @@ private final class ClassDefChecker(classDef: ClassDef, } private def checkAllowTransients()(implicit ctx: ErrorContext): Unit = { - if (!allowTransients) + if (!postOptimizer) reportError("invalid transient tree") } @@ -897,9 +908,9 @@ object ClassDefChecker { * * @return Count of IR checking errors (0 in case of success) */ - def check(classDef: ClassDef, allowReflectiveProxies: Boolean, allowTransients: Boolean, logger: Logger): Int = { + def check(classDef: ClassDef, postBaseLinker: Boolean, postOptimizer: Boolean, logger: Logger): Int = { val reporter = new LoggerErrorReporter(logger) - new ClassDefChecker(classDef, allowReflectiveProxies, allowTransients, reporter).checkClassDef() + new ClassDefChecker(classDef, postBaseLinker, postOptimizer, reporter).checkClassDef() reporter.errorCount } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index 7589e8b651..692eb8a7f1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -147,15 +147,11 @@ private[frontend] object BaseLinker { val allMethods = methods ++ syntheticMethodDefs - val kind = - if (classInfo.isModuleAccessed) classDef.kind - else classDef.kind.withoutModuleAccessor - val ancestors = classInfo.ancestors.map(_.className) val linkedClass = new LinkedClass( classDef.name, - kind, + classDef.kind, classDef.jsClassCaptures, classDef.superClass, classDef.interfaces, diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/ReflectionTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/ReflectionTest.scala index d861a64c55..852b841a02 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/ReflectionTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/ReflectionTest.scala @@ -19,6 +19,8 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ +import org.scalajs.testsuite.utils.AssertThrows.assertThrows + /** Tests the little reflection we support */ class ReflectionTest { import ReflectionTest._ @@ -131,6 +133,112 @@ class ReflectionTest { if (hiddenJSObj.getClass() != null) fail("optimizer thought that hiddenJSObj.getClass() was non-null") } + + @Test def jsTypesKeptOnlyForTheirData_Issue4850(): Unit = { + /* Note: it is not possible to write `classOf[SomeObject.type]`. In order + * to get the class data of module classes (`object`s) without + * instantiating them or reaching anything else about them, we go through + * the `classOf` of an `Array[SomeObject.type]` then extract its + * `getComponentType()`. + */ + + import JSTypesKeptOnlyForTheirData._ + + @noinline + def nameOf(cls: Class[_]): String = cls.getName() + + @inline + def testName(expectedShortName: String, cls: Class[_]): Unit = { + val prefix = "org.scalajs.testsuite.compiler.ReflectionTest$JSTypesKeptOnlyForTheirData$" + val expectedName = prefix + expectedShortName + + assertEquals(expectedName, cls.getName()) // constant-folded + assertEquals(expectedName, nameOf(cls)) // evaluated at run-time + } + + testName("NativeClass", classOf[NativeClass]) + testName("NativeObject$", classOf[Array[NativeObject.type]].getComponentType()) + testName("NativeTrait", classOf[NativeTrait]) + testName("NonNativeClass", classOf[NonNativeClass]) + testName("NonNativeObject$", classOf[Array[NonNativeObject.type]].getComponentType()) + testName("NonNativeTrait", classOf[NonNativeTrait]) + + @noinline + def isInterfaceOf(cls: Class[_]): Boolean = cls.isInterface() + + @inline + def testIsInterface(expected: Boolean, cls: Class[_]): Unit = { + assertEquals(expected, cls.isInterface()) // could be constant-folded in the future + assertEquals(expected, isInterfaceOf(cls)) // evaluated at run-time + } + + // Consistent with isInterfaceForInstantiatedJSTypes() + testIsInterface(false, classOf[NativeClass]) + testIsInterface(false, classOf[Array[NativeObject.type]].getComponentType()) + testIsInterface(false, classOf[NativeTrait]) + testIsInterface(false, classOf[NonNativeClass]) + testIsInterface(false, classOf[Array[NonNativeObject.type]].getComponentType()) + testIsInterface(false, classOf[NonNativeTrait]) + + @noinline + def isInstance(cls: Class[_], x: Any): Boolean = cls.isInstance(x) + + @inline + def testIsInstance(expected: Boolean, cls: Class[_], x: Any): Unit = { + assertEquals(expected, cls.isInstance(x)) + assertEquals(expected, isInstance(cls, x)) + } + + @inline + def testIsInstanceThrows(expected: js.Dynamic, cls: Class[_], x: Any): Unit = { + val e1 = assertThrows(classOf[js.JavaScriptException], cls.isInstance(x)) + assertTrue(e1.toString(), js.special.instanceof(e1.exception, expected)) + + val e2 = assertThrows(classOf[js.JavaScriptException], isInstance(cls, x)) + assertTrue(e2.toString(), js.special.instanceof(e2.exception, expected)) + } + + val jsDate: Any = new js.Date(1684246473882.0) + + testIsInstance(false, classOf[NonNativeClass], jsDate) + testIsInstance(true, classOf[JSDateForIsInstance], jsDate) + + // NativeClass is not actually defined, so isInstance will throw a ReferenceError + testIsInstanceThrows(js.constructorOf[js.ReferenceError], classOf[NativeClass], jsDate) + + // isInstance is not supported on JS objects and traits; it throws a TypeError by spec + testIsInstanceThrows(js.constructorOf[js.TypeError], + classOf[Array[NativeObject.type]].getComponentType(), jsDate) + testIsInstanceThrows(js.constructorOf[js.TypeError], classOf[NativeTrait], jsDate) + testIsInstanceThrows(js.constructorOf[js.TypeError], + classOf[Array[NonNativeObject.type]].getComponentType(), jsDate) + testIsInstanceThrows(js.constructorOf[js.TypeError], classOf[NonNativeTrait], jsDate) + testIsInstanceThrows(js.constructorOf[js.TypeError], + classOf[Array[JSDateForIsInstance.type]].getComponentType(), jsDate) + } + + @Test def isInterfaceForInstantiatedJSTypes(): Unit = { + // Make sure the instantiated non-native things are actually instantiated + assertEquals("function", js.typeOf(js.constructorOf[InstantiatedNonNativeClass])) + assertEquals("object", js.typeOf(InstantiatedNonNativeObject)) + + @noinline + def isInterfaceOf(cls: Class[_]): Boolean = cls.isInterface() + + @inline + def testIsInterface(expected: Boolean, cls: Class[_]): Unit = { + assertEquals(expected, cls.isInterface()) // could be constant-folded in the future + assertEquals(expected, isInterfaceOf(cls)) // evaluated at run-time + } + + // Consistent with jsTypesKeptOnlyForTheirData_Issue4850() + testIsInterface(false, classOf[js.Date]) // native class + testIsInterface(false, classOf[Array[js.Math.type]].getComponentType()) // native object + testIsInterface(false, classOf[js.Function0[Any]]) // native trait + testIsInterface(false, classOf[InstantiatedNonNativeClass]) + testIsInterface(false, classOf[Array[InstantiatedNonNativeObject.type]].getComponentType()) + testIsInterface(false, classOf[PseudoInstantiatedNonNativeTrait]) + } } object ReflectionTest { @@ -143,4 +251,36 @@ object ReflectionTest { class OtherPrefixRenamedTestClass + object JSTypesKeptOnlyForTheirData { + @js.native + @JSGlobal("NativeClass") + class NativeClass extends js.Object + + @js.native + @JSGlobal("NativeObject") + object NativeObject extends js.Object + + @js.native + trait NativeTrait extends js.Object + + class NonNativeClass extends js.Object + + object NonNativeObject extends js.Object + + trait NonNativeTrait extends js.Object + + @js.native + @JSGlobal("Date") + class JSDateForIsInstance extends js.Object + + @js.native + @JSGlobal("Date") + object JSDateForIsInstance extends js.Object + } + + trait PseudoInstantiatedNonNativeTrait extends js.Object + + class InstantiatedNonNativeClass extends js.Object with PseudoInstantiatedNonNativeTrait + + object InstantiatedNonNativeObject extends js.Object with PseudoInstantiatedNonNativeTrait } From cfd7bd62a244417d719d99fdc8b4712dbc2b2210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 22 May 2023 18:29:16 +0200 Subject: [PATCH 14/27] Remove Analysis.ClassInfo.isModuleAccessed. It is never used anymore. `isInstantiated` can be used instead. --- .../src/main/scala/org/scalajs/linker/analyzer/Analysis.scala | 4 ++-- .../src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala index 116fdb62d3..46050e65b1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala @@ -57,11 +57,11 @@ object Analysis { def nonExistent: Boolean /** For a Scala class, it is instantiated with a `New`; for a JS class, * its constructor is accessed with a `JSLoadConstructor` or because it - * is needed for a subclass. + * is needed for a subclass. For modules (Scala or JS), the module is + * accessed. */ def isInstantiated: Boolean def isAnySubclassInstantiated: Boolean - def isModuleAccessed: Boolean def areInstanceTestsUsed: Boolean def isDataAccessed: Boolean diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index 8b2b4adbd6..e6deaa65ab 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -582,7 +582,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, var isInstantiated: Boolean = false var isAnySubclassInstantiated: Boolean = false - var isModuleAccessed: Boolean = false + private var isModuleAccessed: Boolean = false var areInstanceTestsUsed: Boolean = false var isDataAccessed: Boolean = false From baaf8c9bf430f512c499402185f0b5c0c612637d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 26 May 2023 12:38:48 +0200 Subject: [PATCH 15/27] Fix #4870: Handle large (pos and neg) counts in BigInteger.shift{Left,Right}. --- javalib/src/main/scala/java/math/BigInteger.scala | 9 +++++++-- javalib/src/main/scala/java/math/BitLevel.scala | 5 +++-- .../javalib/math/BigIntegerOperateBitsTest.scala | 13 +++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/javalib/src/main/scala/java/math/BigInteger.scala b/javalib/src/main/scala/java/math/BigInteger.scala index 9a9e328742..dcae03d0f9 100644 --- a/javalib/src/main/scala/java/math/BigInteger.scala +++ b/javalib/src/main/scala/java/math/BigInteger.scala @@ -130,6 +130,11 @@ object BigInteger { throw new IllegalArgumentException(errorMessage) } + private[math] def checkRangeBasedOnIntArrayLength(byteLength: Int): Unit = { + if (byteLength < 0 || byteLength >= ((Int.MaxValue + 1) >>> 5)) + throw new ArithmeticException("BigInteger would overflow supported range") + } + @inline private[math] final class QuotAndRem(val quot: BigInteger, val rem: BigInteger) { def toArray(): Array[BigInteger] = Array[BigInteger](quot, rem) @@ -668,13 +673,13 @@ class BigInteger extends Number with Comparable[BigInteger] { def shiftLeft(n: Int): BigInteger = { if (n == 0 || sign == 0) this else if (n > 0) BitLevel.shiftLeft(this, n) - else BitLevel.shiftRight(this, -n) + else BitLevel.shiftRight(this, -n) // -n is interpreted as unsigned, so MinValue is fine } def shiftRight(n: Int): BigInteger = { if (n == 0 || sign == 0) this else if (n > 0) BitLevel.shiftRight(this, n) - else BitLevel.shiftLeft(this, -n) + else BitLevel.shiftLeft(this, -n) // -n is interpreted as unsigned, so MinValue is fine } def signum(): Int = sign diff --git a/javalib/src/main/scala/java/math/BitLevel.scala b/javalib/src/main/scala/java/math/BitLevel.scala index e1e3dae417..1b83daa754 100644 --- a/javalib/src/main/scala/java/math/BitLevel.scala +++ b/javalib/src/main/scala/java/math/BitLevel.scala @@ -220,10 +220,11 @@ private[math] object BitLevel { * @return */ def shiftLeft(source: BigInteger, count: Int): BigInteger = { - val intCount: Int = count >> 5 + val intCount: Int = count >>> 5 // interpret count as unsigned to deal with -MinValue val andCount: Int = count & 31 val offset = if (andCount == 0) 0 else 1 val resLength: Int = source.numberLength + intCount + offset + BigInteger.checkRangeBasedOnIntArrayLength(resLength) val resDigits = new Array[Int](resLength) shiftLeft(resDigits, source.digits, intCount, andCount) val result = new BigInteger(source.sign, resLength, resDigits) @@ -298,7 +299,7 @@ private[math] object BitLevel { * @return */ def shiftRight(source: BigInteger, count: Int): BigInteger = { - val intCount: Int = count >> 5 + val intCount: Int = count >>> 5 // interpret count as unsigned to deal with -MinValue val andCount: Int = count & 31 // count of remaining bits if (intCount >= source.numberLength) { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerOperateBitsTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerOperateBitsTest.scala index 8939f72c83..ad9d961e26 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerOperateBitsTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerOperateBitsTest.scala @@ -1029,6 +1029,19 @@ class BigIntegerOperateBitsTest { assertEquals(-1, result.signum()) } + @Test def testShiftsWithLargeCounts_Issue4870(): Unit = { + val aNumber = new BigInteger(-1, Array[Byte](0, -82, 127, 15, 76, -97, 13, 30, 30)) + + assertThrows(classOf[ArithmeticException], aNumber.shiftLeft(Int.MaxValue)) + assertThrows(classOf[ArithmeticException], aNumber.shiftRight(Int.MinValue)) + assertThrows(classOf[ArithmeticException], aNumber.shiftRight(Int.MinValue + 1)) + + val minusOne = BigInteger.ONE.negate() + assertEquals(minusOne, aNumber.shiftRight(Int.MaxValue)) + assertEquals(minusOne, aNumber.shiftLeft(Int.MinValue)) + assertEquals(minusOne, aNumber.shiftLeft(Int.MinValue + 1)) + } + @Test def testTestBitException(): Unit = { val aBytes = Array[Byte](-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26) val aSign = 1 From a4d9801bed67f564a825fd020abe26edc39cd19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Tue, 30 May 2023 14:19:18 +0200 Subject: [PATCH 16/27] Fix #4872: Implement java.util.StringJoiner. --- .../main/scala/java/util/StringJoiner.scala | 62 +++++++ .../javalib/util/StringJoinerTest.scala | 168 ++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 javalib/src/main/scala/java/util/StringJoiner.scala create mode 100644 test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/StringJoinerTest.scala diff --git a/javalib/src/main/scala/java/util/StringJoiner.scala b/javalib/src/main/scala/java/util/StringJoiner.scala new file mode 100644 index 0000000000..6359910260 --- /dev/null +++ b/javalib/src/main/scala/java/util/StringJoiner.scala @@ -0,0 +1,62 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package java.util + +@inline +final class StringJoiner private (delimiter: String, prefix: String, suffix: String) extends AnyRef { + /** The custom value to return if empty, set by `setEmptyValue` (nullable). + * + * If `null`, defaults to `prefix + suffix`. + */ + private var emptyValue: String = null + + /** The current value, excluding prefix and suffix. */ + private var value: String = "" + + /** Whether the string joiner is currently empty. */ + private var isEmpty: Boolean = true + + def this(delimiter: CharSequence) = + this(delimiter.toString(), "", "") + + def this(delimiter: CharSequence, prefix: CharSequence, suffix: CharSequence) = + this(delimiter.toString(), prefix.toString(), suffix.toString()) + + def setEmptyValue(emptyValue: CharSequence): StringJoiner = { + this.emptyValue = emptyValue.toString() + this + } + + override def toString(): String = + if (isEmpty && emptyValue != null) emptyValue + else prefix + value + suffix + + def add(newElement: CharSequence): StringJoiner = { + if (isEmpty) + isEmpty = false + else + value += delimiter + value += newElement // if newElement is null, adds "null" + this + } + + def merge(other: StringJoiner): StringJoiner = { + if (!other.isEmpty) // if `other` is empty, `merge` has no effect + add(other.value) // without prefix nor suffix, but with delimiters + this + } + + def length(): Int = + if (isEmpty && emptyValue != null) emptyValue.length() + else prefix.length() + value.length() + suffix.length() +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/StringJoinerTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/StringJoinerTest.scala new file mode 100644 index 0000000000..77ceec005d --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/StringJoinerTest.scala @@ -0,0 +1,168 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.javalib.util + +import java.nio.CharBuffer +import java.util.StringJoiner + +import org.junit.Assert._ +import org.junit.Assume._ +import org.junit.Test + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform + +class StringJoinerTest { + import StringJoinerTest._ + + @Test def testEmpty(): Unit = { + assertJoinerResult("")(new StringJoiner(",")) + assertJoinerResult("[]")(new StringJoiner(";", "[", "]")) + assertJoinerResult("--")(new StringJoiner(";").setEmptyValue("--")) + assertJoinerResult("--")(new StringJoiner(";", "[", "]").setEmptyValue("--")) + } + + @Test def testNonEmpty(): Unit = { + assertJoinerResult("one") { + new StringJoiner(",").add("one") + } + assertJoinerResult("one,two,three") { + new StringJoiner(",").add("one").add("two").add("three") + } + assertJoinerResult("[one, two, three]") { + new StringJoiner(", ", "[", "]").add("one").add("two").add("three") + } + assertJoinerResult("[one, null, three]") { + new StringJoiner(", ", "[", "]").add("one").add(null).add("three") + } + assertJoinerResult("[one, two, three]") { + new StringJoiner(", ", "[", "]").add("one").add(CharBuffer.wrap("two")).add("three") + } + assertJoinerResult("") { + new StringJoiner(",").add("") + } + assertJoinerResult("one,") { + new StringJoiner(",").add("one").add("") + } + assertJoinerResult(",two") { + new StringJoiner(",").add("").add("two") + } + assertJoinerResult("one,,three") { + new StringJoiner(",").add("one").add("").add("three") + } + + assertJoinerResult("one") { + new StringJoiner(",").setEmptyValue("--").add("one") + } + assertJoinerResult("one,two,three") { + new StringJoiner(",").setEmptyValue("--").add("one").add("two").add("three") + } + assertJoinerResult("[one, two, three]") { + new StringJoiner(", ", "[", "]").add("one").add("two").setEmptyValue("--").add("three") + } + assertJoinerResult("[one, two, three]") { + new StringJoiner(", ", "[", "]").add("one").add("two").add("three").setEmptyValue("--") + } + assertJoinerResult("") { + new StringJoiner(",").setEmptyValue("--").add("") + } + assertJoinerResult("one,") { + new StringJoiner(",").setEmptyValue("--").add("one").add("") + } + } + + @Test def testMerge(): Unit = { + val empty = new StringJoiner(";", "[", "]").setEmptyValue("--") + val single = new StringJoiner(";", "[", "]").setEmptyValue("--").add("single") + val multiple = new StringJoiner(";", "[", "]").setEmptyValue("--").add("a").add("b").add("c") + val singleBlank = new StringJoiner(";", "[", "]").setEmptyValue("--").add("") + + assertJoinerResult("+++") { + new StringJoiner(", ", "{", "}").merge(empty).setEmptyValue("+++") + } + assertJoinerResult("+++") { + new StringJoiner(", ", "{", "}").setEmptyValue("+++").merge(empty) + } + assertJoinerResult("{}") { + new StringJoiner(", ", "{", "}").merge(singleBlank).setEmptyValue("+++") + } + assertJoinerResult("{}") { + new StringJoiner(", ", "{", "}").setEmptyValue("+++").merge(singleBlank) + } + assertJoinerResult("{one, two}") { + new StringJoiner(", ", "{", "}").add("one").merge(empty).add("two") + } + assertJoinerResult("{one, single, two}") { + new StringJoiner(", ", "{", "}").add("one").merge(single).add("two") + } + assertJoinerResult("{one, a;b;c, two}") { + new StringJoiner(", ", "{", "}").add("one").merge(multiple).add("two") + } + assertJoinerResult("{one, , two}") { + new StringJoiner(", ", "{", "}").add("one").merge(singleBlank).add("two") + } + assertJoinerResult("{single}") { + new StringJoiner(", ", "{", "}").merge(single) + } + assertJoinerResult("{a;b;c}") { + new StringJoiner(", ", "{", "}").merge(multiple) + } + assertJoinerResult("{}") { + new StringJoiner(", ", "{", "}").merge(singleBlank) + } + } + + @Test def testState(): Unit = { + val mutableCharSeq = CharBuffer.allocate(2).put(0, '?').put(1, '!') + + val joiner = new StringJoiner(mutableCharSeq, mutableCharSeq, mutableCharSeq) + assertJoinerResult("?!?!")(joiner) + joiner.setEmptyValue(mutableCharSeq) + assertJoinerResult("?!")(joiner) + + mutableCharSeq.put(0, '-') + assertJoinerResult("?!")(joiner) // the previously set emptyValue is not affected + joiner.setEmptyValue(mutableCharSeq) + assertJoinerResult("-!")(joiner) + + joiner.add("one") + assertJoinerResult("?!one?!")(joiner) // the previously set prefix and suffix are not affected + + joiner.add("two") + assertJoinerResult("?!one?!two?!")(joiner) // the previously set delimiter is not affected + } + + @Test def testNPE(): Unit = { + assumeTrue("requires compliant null pointers", Platform.hasCompliantNullPointers) + + @noinline + def assertNPE[U](code: => U): Unit = + assertThrows(classOf[NullPointerException], code) + + assertNPE(new StringJoiner(null)) + assertNPE(new StringJoiner(null, "[", "]")) + assertNPE(new StringJoiner(",", null, "]")) + assertNPE(new StringJoiner(",", "[", null)) + + assertNPE(new StringJoiner(",").setEmptyValue(null)) + + assertNPE(new StringJoiner(",").merge(null)) + } +} + +object StringJoinerTest { + def assertJoinerResult(expected: String)(joiner: StringJoiner): Unit = { + assertEquals(expected, joiner.toString()) + assertEquals(expected.length(), joiner.length()) + } +} From 18a98aa0e7dc0d781a269711a54ee387c693f6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 2 Jun 2023 17:49:34 +0200 Subject: [PATCH 17/27] Upgrade to sbt 1.9.0. sbt 1.9.0 contains the new sbt plugin publishing mechanism. It dual-publishes sbt plugins with Ivy-style paths (invalid in Maven, although it works out when released from sbt) and valid Maven paths. See https://github.com/sbt/sbt/issues/3410 and https://github.com/sbt/sbt/pull/7096. --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index e64c208ff5..40b3b8e7b6 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.8 +sbt.version=1.9.0 From 3cd29288ece73256110879ad11dfa5e1c3ff5a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Sun, 4 Jun 2023 16:47:48 +0200 Subject: [PATCH 18/27] Upgrade to sbt-platform-deps 1.0.2. To get dual-publishing across the dependency chain. --- project/Build.scala | 2 +- project/build.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index a272ecc0a7..04e7cf9df7 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1200,7 +1200,7 @@ object Build { previousArtifactSetting, mimaBinaryIssueFilters ++= BinaryIncompatibilities.SbtPlugin, - addSbtPlugin("org.portable-scala" % "sbt-platform-deps" % "1.0.1"), + addSbtPlugin("org.portable-scala" % "sbt-platform-deps" % "1.0.2"), libraryDependencies += "org.scala-js" %% "scalajs-js-envs" % "1.4.0", libraryDependencies += "org.scala-js" %% "scalajs-env-nodejs" % "1.4.0", diff --git a/project/build.sbt b/project/build.sbt index 240ffb135c..b676f34e52 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -4,7 +4,7 @@ addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") addSbtPlugin("org.scalastyle" % "scalastyle-sbt-plugin" % "1.0.0") -addSbtPlugin("org.portable-scala" % "sbt-platform-deps" % "1.0.1") +addSbtPlugin("org.portable-scala" % "sbt-platform-deps" % "1.0.2") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") From af0c7e67c0219f2d1dc95b530225cca24f677698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 5 Jun 2023 11:54:25 +0200 Subject: [PATCH 19/27] Fix #4875: Change our strategy for crossScalaVersions handling. sbt 1.7.0 made `++` even stricter than before. It now only applies if the target version appears as is in `crossScalaVersions`, rather than a binary-compatible one. We now introduce specific keys to hold a list of minor versions we support for each major version. As keys, they can be overridden with `set` commands if we want to test nightly versions of Scala. We also make sure to apply the full `crossScalaVersions` list even in projects that should only build with the default 2.12 version, like the `javalib`. Otherwise, tests with other minor 2.12 versions refuse to resolve because it is not the same 2.12 version everywhere. --- project/Build.scala | 88 +++++++++++++++++++++++++++------ project/MultiScalaProject.scala | 57 +++++---------------- 2 files changed, 85 insertions(+), 60 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 04e7cf9df7..dd30fc7f7c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -39,6 +39,17 @@ import org.scalajs.linker.interface._ */ object ExposedValues extends AutoPlugin { object autoImport { + val cross212ScalaVersions: SettingKey[Seq[String]] = + settingKey("an ordered sequence of 2.12.x versions with which we build (most recent last)") + + val cross213ScalaVersions: SettingKey[Seq[String]] = + settingKey("an ordered sequence of 2.13.x versions with which we build (most recent last)") + + val default212ScalaVersion: SettingKey[String] = + settingKey("the default Scala 2.12.x version for this build (derived from cross212ScalaVersions)") + val default213ScalaVersion: SettingKey[String] = + settingKey("the default Scala 2.13.x version for this build (derived from cross213ScalaVersions)") + // set scalaJSLinkerConfig in someProject ~= makeCompliant val makeCompliant: StandardConfig => StandardConfig = { _.withSemantics { semantics => @@ -224,18 +235,19 @@ object MyScalaJSPlugin extends AutoPlugin { } object Build { + import ExposedValues.autoImport.{ + cross212ScalaVersions, + cross213ScalaVersions, + default212ScalaVersion, + default213ScalaVersion + } + import MyScalaJSPlugin.{ scalaJSCompilerOption, scalaJSMapSourceURIOption, isGeneratingForIDE } - import MultiScalaProject.{ - Default2_12ScalaVersion, - Default2_13ScalaVersion, - DefaultScalaVersion - } - val scalastyleCheck = taskKey[Unit]("Run scalastyle") val fetchScalaSource = taskKey[File]( @@ -260,8 +272,6 @@ object Build { val previousBinaryCrossVersion = CrossVersion.binaryWith("sjs1_", "") - val scalaVersionsUsedForPublishing: Set[String] = - Set(Default2_12ScalaVersion, Default2_13ScalaVersion) val newScalaBinaryVersionsInThisRelease: Set[String] = Set() @@ -317,6 +327,8 @@ object Build { mimaPreviousArtifacts ++= { val scalaV = scalaVersion.value val scalaBinaryV = scalaBinaryVersion.value + val scalaVersionsUsedForPublishing: Set[String] = + Set(default212ScalaVersion.value, default213ScalaVersion.value) if (!scalaVersionsUsedForPublishing.contains(scalaV)) { // This artifact will not be published. Binary compatibility is irrelevant. Set.empty @@ -502,6 +514,17 @@ object Build { } ) + private val defaultScalaVersionOnlySettings = Def.settings( + /* We still need to support all cross versions, otherwise ++2.12.x creates + * inconsistent graphs. + * We use 2.12.x as default version because of the sbt plugin, which must + * use 2.12.x. If we use another default version, importing in IDEs creates + * difficult configurations. + */ + crossScalaVersions := cross212ScalaVersions.value, + scalaVersion := default212ScalaVersion.value, + ) + private val basePublishSettings = Seq( publishMavenStyle := true, publishTo := { @@ -763,6 +786,41 @@ object Build { } val thisBuildSettings = Def.settings( + cross212ScalaVersions := Seq( + "2.12.2", + "2.12.3", + "2.12.4", + "2.12.5", + "2.12.6", + "2.12.7", + "2.12.8", + "2.12.9", + "2.12.10", + "2.12.11", + "2.12.12", + "2.12.13", + "2.12.14", + "2.12.15", + "2.12.16", + "2.12.17", + ), + cross213ScalaVersions := Seq( + "2.13.0", + "2.13.1", + "2.13.2", + "2.13.3", + "2.13.4", + "2.13.5", + "2.13.6", + "2.13.7", + "2.13.8", + "2.13.9", + "2.13.10", + ), + + default212ScalaVersion := cross212ScalaVersions.value.last, + default213ScalaVersion := cross213ScalaVersions.value.last, + // JDK version we are running with javaVersion in Global := { val fullVersion = System.getProperty("java.version") @@ -1005,7 +1063,7 @@ object Build { MyScalaJSPlugin ).settings( commonSettings, - scalaVersion := DefaultScalaVersion, + defaultScalaVersionOnlySettings, fatalWarningsSettings, name := "Scala.js linker private library", publishArtifact in Compile := false, @@ -1192,8 +1250,7 @@ object Build { name := "Scala.js sbt plugin", normalizedName := "sbt-scalajs", sbtPlugin := true, - crossScalaVersions := Seq(DefaultScalaVersion), - scalaVersion := crossScalaVersions.value.head, + defaultScalaVersionOnlySettings, sbtVersion := "1.0.0", scalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value), @@ -1297,7 +1354,7 @@ object Build { MyScalaJSPlugin ).settings( commonSettings, - scalaVersion := DefaultScalaVersion, + defaultScalaVersionOnlySettings, fatalWarningsSettings, name := "scalajs-javalib-internal", publishArtifact in Compile := false, @@ -1787,8 +1844,11 @@ object Build { }, MyScalaJSPlugin.expectedSizes := { + val default212Version = default212ScalaVersion.value + val default213Version = default213ScalaVersion.value + scalaVersion.value match { - case Default2_12ScalaVersion => + case `default212Version` => Some(ExpectedSizes( fastLink = 772000 to 773000, fullLink = 145000 to 146000, @@ -1796,7 +1856,7 @@ object Build { fullLinkGz = 35000 to 36000, )) - case Default2_13ScalaVersion => + case `default213Version` => Some(ExpectedSizes( fastLink = 456000 to 457000, fullLink = 97000 to 98000, diff --git a/project/MultiScalaProject.scala b/project/MultiScalaProject.scala index deb5f0c163..86eb42a867 100644 --- a/project/MultiScalaProject.scala +++ b/project/MultiScalaProject.scala @@ -79,68 +79,33 @@ object MultiScalaProject { private def strictMapValues[K, U, V](v: Map[K, U])(f: U => V): Map[K, V] = v.map(v => (v._1, f(v._2))) - private final val versions = Map[String, Seq[String]]( - "2.12" -> Seq( - "2.12.2", - "2.12.3", - "2.12.4", - "2.12.5", - "2.12.6", - "2.12.7", - "2.12.8", - "2.12.9", - "2.12.10", - "2.12.11", - "2.12.12", - "2.12.13", - "2.12.14", - "2.12.15", - "2.12.16", - "2.12.17", - ), - "2.13" -> Seq( - "2.13.0", - "2.13.1", - "2.13.2", - "2.13.3", - "2.13.4", - "2.13.5", - "2.13.6", - "2.13.7", - "2.13.8", - "2.13.9", - "2.13.10", - ), - ) - - val Default2_12ScalaVersion = versions("2.12").last - val Default2_13ScalaVersion = versions("2.13").last - - /** The default Scala version is the default 2.12 Scala version, because it - * must work for sbt plugins. - */ - val DefaultScalaVersion = Default2_12ScalaVersion - private final val ideVersion = "2.12" private def projectID(id: String, major: String) = id + major.replace('.', '_') def apply(id: String, base: File): MultiScalaProject = { + import ExposedValues.autoImport._ + val projects = for { - (major, minors) <- versions + major <- List("2.12", "2.13") } yield { val noIDEExportSettings = if (major == ideVersion) Nil else NoIDEExport.noIDEExportSettings + val (crossVersionsKey, defaultVersionKey) = major match { + case "2.12" => (cross212ScalaVersions, default212ScalaVersion) + case "2.13" => (cross213ScalaVersions, default213ScalaVersion) + } + major -> Project(id = projectID(id, major), base = new File(base, "." + major)).settings( - scalaVersion := minors.last, - crossScalaVersions := minors, + crossScalaVersions := crossVersionsKey.value, + scalaVersion := defaultVersionKey.value, noIDEExportSettings, ) } - new MultiScalaProject(projects).settings( + new MultiScalaProject(projects.toMap).settings( sourceDirectory := baseDirectory.value.getParentFile / "src", ) } From 18df64bb03ceb87a28f256bf99bda22e6429d550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 5 Jun 2023 16:28:06 +0200 Subject: [PATCH 20/27] Remove support for Scala 2.12.4. It simply has not been supported by the Zinc used since sbt 1.6.0. See https://github.com/sbt/sbt/issues/6838 and https://github.com/sbt/zinc/issues/1010 . Attempting to compile *any* project using Scala 2.12.4 and sbt 1.6.0+, including a Hello World, results in a StackOverflow. Therefore, no one must be using Scala 2.12.4 at this point. --- Jenkinsfile | 1 - project/Build.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7f87dfdae4..67e6008355 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -457,7 +457,6 @@ def mainScalaVersions = ["2.12.17", "2.13.10"] def otherScalaVersions = [ "2.12.2", "2.12.3", - "2.12.4", "2.12.5", "2.12.6", "2.12.7", diff --git a/project/Build.scala b/project/Build.scala index dd30fc7f7c..736f012d53 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -789,7 +789,6 @@ object Build { cross212ScalaVersions := Seq( "2.12.2", "2.12.3", - "2.12.4", "2.12.5", "2.12.6", "2.12.7", From ab060878ce7b7d442711b71eec6fdc4647e317a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 5 Jun 2023 13:14:01 +0200 Subject: [PATCH 21/27] Upgrade to Scala 2.12.18. --- Jenkinsfile | 5 +- .../{2.12.17 => 2.12.18}/BlacklistedTests.txt | 5 + .../{2.12.17 => 2.12.18}/neg/choices.check | 0 .../neg/partestInvalidFlag.check | 0 .../{2.12.17 => 2.12.18}/neg/t11952b.check | 0 .../neg/t6446-additional.check | 0 .../{2.12.17 => 2.12.18}/neg/t6446-list.check | 0 .../neg/t6446-missing.check | 0 .../neg/t6446-show-phases.check | 0 .../neg/t7494-no-options.check | 0 .../run/Course-2002-01.check | 0 .../run/Course-2002-02.check | 0 .../run/Course-2002-04.check | 0 .../run/Course-2002-08.check | 0 .../run/Course-2002-09.check | 0 .../run/Course-2002-10.check | 0 .../{2.12.17 => 2.12.18}/run/Meter.check | 0 .../run/MeterCaseClass.check | 0 .../run/anyval-box-types.check | 0 .../scalajs/{2.12.17 => 2.12.18}/run/bugs.sem | 0 .../run/caseClassHash.check | 0 .../{2.12.17 => 2.12.18}/run/classof.check | 0 .../{2.12.17 => 2.12.18}/run/deeps.check | 0 .../run/dynamic-anyval.check | 0 .../{2.12.17 => 2.12.18}/run/exceptions-2.sem | 0 .../run/exceptions-nest.check | 0 .../run/exceptions-nest.sem | 0 .../run/impconvtimes.check | 0 .../{2.12.17 => 2.12.18}/run/imports.check | 0 .../run/inlineHandlers.sem | 0 .../run/interpolation.check | 0 .../run/interpolationMultiline1.check | 0 .../run/macro-bundle-static.check | 0 .../run/macro-bundle-toplevel.check | 0 .../run/macro-bundle-whitebox-decl.check | 0 .../{2.12.17 => 2.12.18}/run/misc.check | 0 .../run/optimizer-array-load.sem | 0 .../{2.12.17 => 2.12.18}/run/pf-catch.sem | 0 .../{2.12.17 => 2.12.18}/run/promotion.check | 0 .../{2.12.17 => 2.12.18}/run/runtime.check | 0 .../{2.12.17 => 2.12.18}/run/spec-self.check | 0 .../{2.12.17 => 2.12.18}/run/structural.check | 0 .../{2.12.17 => 2.12.18}/run/t0421-new.check | 0 .../{2.12.17 => 2.12.18}/run/t0421-old.check | 0 .../{2.12.17 => 2.12.18}/run/t1503.sem | 0 .../{2.12.17 => 2.12.18}/run/t3702.check | 0 .../{2.12.17 => 2.12.18}/run/t4148.sem | 0 .../{2.12.17 => 2.12.18}/run/t4617.check | 0 .../{2.12.17 => 2.12.18}/run/t5356.check | 0 .../{2.12.17 => 2.12.18}/run/t5552.check | 0 .../{2.12.17 => 2.12.18}/run/t5568.check | 0 .../{2.12.17 => 2.12.18}/run/t5629b.check | 0 .../{2.12.17 => 2.12.18}/run/t5680.check | 0 .../{2.12.17 => 2.12.18}/run/t5866.check | 0 .../run/t6318_primitives.check | 0 .../{2.12.17 => 2.12.18}/run/t6662.check | 0 .../{2.12.17 => 2.12.18}/run/t6827.sem | 0 .../{2.12.17 => 2.12.18}/run/t7657.check | 0 .../{2.12.17 => 2.12.18}/run/t7763.sem | 0 .../{2.12.17 => 2.12.18}/run/t8570a.check | 0 .../{2.12.17 => 2.12.18}/run/t8601b.sem | 0 .../{2.12.17 => 2.12.18}/run/t8601c.sem | 0 .../{2.12.17 => 2.12.18}/run/t8601d.sem | 0 .../{2.12.17 => 2.12.18}/run/t8764.check | 0 .../{2.12.17 => 2.12.18}/run/t9387b.check | 0 .../{2.12.17 => 2.12.18}/run/t9656.check | 0 .../run/try-catch-unify.check | 0 .../run/virtpatmat_switch.check | 0 .../run/virtpatmat_typetag.check | 0 project/Build.scala | 1 + .../change-config-and-source/build.sbt | 2 +- .../incremental/change-config/build.sbt | 2 +- .../incremental/fix-compile-error/build.sbt | 2 +- .../linker/concurrent-linker-use/build.sbt | 2 +- .../sbt-test/linker/custom-linker/build.sbt | 4 +- .../no-root-dependency-resolution/build.sbt | 2 +- .../linker/non-existent-classpath/build.sbt | 2 +- .../sbt-test/settings/cross-version/build.sbt | 2 +- .../src/sbt-test/settings/env-vars/build.sbt | 2 +- .../settings/legacy-link-empty/build.sbt | 2 +- .../settings/legacy-link-tasks/build.sbt | 2 +- .../sbt-test/settings/module-init/build.sbt | 2 +- .../sbt-test/settings/source-map/build.sbt | 2 +- .../testing/multi-framework/build.sbt | 2 +- .../resources/2.12.18/BlacklistedTests.txt | 196 ++++++++++++++++++ 85 files changed, 220 insertions(+), 17 deletions(-) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/BlacklistedTests.txt (99%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/choices.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/partestInvalidFlag.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/t11952b.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/t6446-additional.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/t6446-list.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/t6446-missing.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/t6446-show-phases.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/neg/t7494-no-options.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Course-2002-01.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Course-2002-02.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Course-2002-04.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Course-2002-08.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Course-2002-09.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Course-2002-10.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/Meter.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/MeterCaseClass.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/anyval-box-types.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/bugs.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/caseClassHash.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/classof.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/deeps.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/dynamic-anyval.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/exceptions-2.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/exceptions-nest.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/exceptions-nest.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/impconvtimes.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/imports.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/inlineHandlers.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/interpolation.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/interpolationMultiline1.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/macro-bundle-static.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/macro-bundle-toplevel.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/macro-bundle-whitebox-decl.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/misc.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/optimizer-array-load.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/pf-catch.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/promotion.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/runtime.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/spec-self.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/structural.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t0421-new.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t0421-old.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t1503.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t3702.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t4148.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t4617.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t5356.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t5552.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t5568.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t5629b.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t5680.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t5866.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t6318_primitives.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t6662.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t6827.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t7657.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t7763.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t8570a.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t8601b.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t8601c.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t8601d.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t8764.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t9387b.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/t9656.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/try-catch-unify.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/virtpatmat_switch.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.12.17 => 2.12.18}/run/virtpatmat_typetag.check (100%) create mode 100644 scala-test-suite/src/test/resources/2.12.18/BlacklistedTests.txt diff --git a/Jenkinsfile b/Jenkinsfile index 67e6008355..ae5e56ca16 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -452,8 +452,8 @@ def otherJavaVersions = ["11", "16"] def allJavaVersions = otherJavaVersions.clone() allJavaVersions << mainJavaVersion -def mainScalaVersion = "2.12.17" -def mainScalaVersions = ["2.12.17", "2.13.10"] +def mainScalaVersion = "2.12.18" +def mainScalaVersions = ["2.12.18", "2.13.10"] def otherScalaVersions = [ "2.12.2", "2.12.3", @@ -469,6 +469,7 @@ def otherScalaVersions = [ "2.12.14", "2.12.15", "2.12.16", + "2.12.17", "2.13.0", "2.13.1", "2.13.2", diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/BlacklistedTests.txt similarity index 99% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/BlacklistedTests.txt index 4a6f7b8ec6..1bb8c9a08d 100644 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/BlacklistedTests.txt @@ -643,6 +643,11 @@ run/t10856.scala run/t12002.scala +run/dotty-i11332.scala +run/dotty-i11332b.scala +run/dotty-t12348.scala +run/t12348.scala + # Uses reflection indirectly through # scala.runtime.ScalaRunTime.replStringOf run/t6634.scala diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/choices.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/choices.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/choices.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/choices.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/partestInvalidFlag.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/partestInvalidFlag.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/partestInvalidFlag.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/partestInvalidFlag.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t11952b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t11952b.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t11952b.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t11952b.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-additional.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-additional.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-additional.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-additional.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-list.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-list.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-list.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-list.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-missing.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-missing.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-missing.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-missing.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-show-phases.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-show-phases.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t6446-show-phases.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t6446-show-phases.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t7494-no-options.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t7494-no-options.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/neg/t7494-no-options.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/neg/t7494-no-options.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-01.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-01.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-01.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-01.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-02.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-02.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-02.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-02.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-04.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-04.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-04.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-04.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-08.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-08.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-08.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-08.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-09.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-09.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-09.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-09.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-10.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-10.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Course-2002-10.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Course-2002-10.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Meter.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Meter.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/Meter.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/Meter.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/MeterCaseClass.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/MeterCaseClass.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/MeterCaseClass.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/MeterCaseClass.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/anyval-box-types.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/anyval-box-types.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/anyval-box-types.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/anyval-box-types.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/bugs.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/bugs.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/bugs.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/bugs.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/caseClassHash.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/caseClassHash.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/caseClassHash.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/caseClassHash.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/classof.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/classof.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/classof.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/classof.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/deeps.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/deeps.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/deeps.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/deeps.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/dynamic-anyval.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/dynamic-anyval.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/dynamic-anyval.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/dynamic-anyval.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-2.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/exceptions-2.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-2.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/exceptions-2.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/exceptions-nest.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/exceptions-nest.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/exceptions-nest.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/exceptions-nest.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/impconvtimes.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/impconvtimes.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/impconvtimes.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/impconvtimes.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/imports.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/imports.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/imports.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/imports.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/inlineHandlers.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/inlineHandlers.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/inlineHandlers.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/inlineHandlers.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/interpolation.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/interpolation.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/interpolation.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/interpolation.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/interpolationMultiline1.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/interpolationMultiline1.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/interpolationMultiline1.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/interpolationMultiline1.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/macro-bundle-static.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/macro-bundle-static.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/macro-bundle-static.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/macro-bundle-static.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/macro-bundle-toplevel.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/macro-bundle-toplevel.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/macro-bundle-toplevel.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/macro-bundle-toplevel.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/macro-bundle-whitebox-decl.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/macro-bundle-whitebox-decl.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/macro-bundle-whitebox-decl.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/macro-bundle-whitebox-decl.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/misc.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/misc.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/misc.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/misc.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/optimizer-array-load.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/optimizer-array-load.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/optimizer-array-load.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/optimizer-array-load.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/pf-catch.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/pf-catch.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/pf-catch.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/pf-catch.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/promotion.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/promotion.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/promotion.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/promotion.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/runtime.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/runtime.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/runtime.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/runtime.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/spec-self.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/spec-self.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/spec-self.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/spec-self.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/structural.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/structural.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/structural.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/structural.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t0421-new.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t0421-new.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t0421-new.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t0421-new.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t0421-old.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t0421-old.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t0421-old.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t0421-old.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t1503.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t1503.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t1503.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t1503.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t3702.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t3702.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t3702.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t3702.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t4148.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t4148.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t4148.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t4148.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t4617.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t4617.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t4617.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t4617.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5356.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5356.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5356.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5356.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5552.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5552.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5552.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5552.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5568.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5568.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5568.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5568.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5629b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5629b.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5629b.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5629b.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5680.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5680.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5680.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5680.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5866.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5866.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t5866.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t5866.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6318_primitives.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t6318_primitives.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6318_primitives.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t6318_primitives.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6662.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t6662.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6662.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t6662.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6827.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t6827.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6827.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t6827.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t7657.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t7657.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t7657.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t7657.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t7763.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t7763.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t7763.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t7763.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8570a.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8570a.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8570a.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8570a.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601b.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8601b.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601b.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8601b.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601c.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8601c.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601c.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8601c.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601d.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8601d.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601d.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8601d.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8764.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8764.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8764.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t8764.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t9387b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t9387b.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t9387b.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t9387b.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t9656.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t9656.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t9656.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/t9656.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/try-catch-unify.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/try-catch-unify.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/try-catch-unify.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/try-catch-unify.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/virtpatmat_switch.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/virtpatmat_switch.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/virtpatmat_switch.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/virtpatmat_switch.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/virtpatmat_typetag.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/virtpatmat_typetag.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/virtpatmat_typetag.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.18/run/virtpatmat_typetag.check diff --git a/project/Build.scala b/project/Build.scala index 736f012d53..5d6664688a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -802,6 +802,7 @@ object Build { "2.12.15", "2.12.16", "2.12.17", + "2.12.18", ), cross213ScalaVersions := Seq( "2.13.0", diff --git a/sbt-plugin/src/sbt-test/incremental/change-config-and-source/build.sbt b/sbt-plugin/src/sbt-test/incremental/change-config-and-source/build.sbt index 89be51b1c5..d12712d68d 100644 --- a/sbt-plugin/src/sbt-test/incremental/change-config-and-source/build.sbt +++ b/sbt-plugin/src/sbt-test/incremental/change-config-and-source/build.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/incremental/change-config/build.sbt b/sbt-plugin/src/sbt-test/incremental/change-config/build.sbt index 89be51b1c5..d12712d68d 100644 --- a/sbt-plugin/src/sbt-test/incremental/change-config/build.sbt +++ b/sbt-plugin/src/sbt-test/incremental/change-config/build.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/incremental/fix-compile-error/build.sbt b/sbt-plugin/src/sbt-test/incremental/fix-compile-error/build.sbt index 89be51b1c5..d12712d68d 100644 --- a/sbt-plugin/src/sbt-test/incremental/fix-compile-error/build.sbt +++ b/sbt-plugin/src/sbt-test/incremental/fix-compile-error/build.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/build.sbt b/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/build.sbt index 0051f274ee..f3238517cd 100644 --- a/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/build.sbt +++ b/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/build.sbt @@ -11,7 +11,7 @@ lazy val concurrentUseOfLinkerTest = taskKey[Any]("") name := "Scala.js sbt test" version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/linker/custom-linker/build.sbt b/sbt-plugin/src/sbt-test/linker/custom-linker/build.sbt index a765f3fd52..2576235cf2 100644 --- a/sbt-plugin/src/sbt-test/linker/custom-linker/build.sbt +++ b/sbt-plugin/src/sbt-test/linker/custom-linker/build.sbt @@ -13,14 +13,14 @@ inThisBuild(Def.settings( version := scalaJSVersion, - scalaVersion := "2.12.17", + scalaVersion := "2.12.18", )) lazy val check = taskKey[Any]("") lazy val customLinker = project.in(file("custom-linker")) .settings( - scalaVersion := "2.12.17", // needs to match the minor version of Scala used by sbt + scalaVersion := "2.12.18", // needs to match the minor version of Scala used by sbt libraryDependencies += "org.scala-js" %% "scalajs-linker" % scalaJSVersion, ) diff --git a/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/build.sbt b/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/build.sbt index 3a19504c82..541d53caf8 100644 --- a/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/build.sbt +++ b/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/build.sbt @@ -1,7 +1,7 @@ name := "Scala.js sbt test" version in ThisBuild := scalaJSVersion -scalaVersion in ThisBuild := "2.12.17" +scalaVersion in ThisBuild := "2.12.18" // Disable the IvyPlugin on the root project disablePlugins(sbt.plugins.IvyPlugin) diff --git a/sbt-plugin/src/sbt-test/linker/non-existent-classpath/build.sbt b/sbt-plugin/src/sbt-test/linker/non-existent-classpath/build.sbt index 7b7c690b41..bf0b1a8bc8 100644 --- a/sbt-plugin/src/sbt-test/linker/non-existent-classpath/build.sbt +++ b/sbt-plugin/src/sbt-test/linker/non-existent-classpath/build.sbt @@ -1,5 +1,5 @@ version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/settings/cross-version/build.sbt b/sbt-plugin/src/sbt-test/settings/cross-version/build.sbt index 921b570e75..98b9b4802a 100644 --- a/sbt-plugin/src/sbt-test/settings/cross-version/build.sbt +++ b/sbt-plugin/src/sbt-test/settings/cross-version/build.sbt @@ -3,7 +3,7 @@ import org.scalajs.sbtplugin.ScalaJSCrossVersion val check = taskKey[Unit]("Run checks of this test") version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" lazy val js = project.enablePlugins(ScalaJSPlugin).settings( check := { diff --git a/sbt-plugin/src/sbt-test/settings/env-vars/build.sbt b/sbt-plugin/src/sbt-test/settings/env-vars/build.sbt index c84d43279b..55967e1eb6 100644 --- a/sbt-plugin/src/sbt-test/settings/env-vars/build.sbt +++ b/sbt-plugin/src/sbt-test/settings/env-vars/build.sbt @@ -1,5 +1,5 @@ inThisBuild(Def.settings( - scalaVersion := "2.12.17", + scalaVersion := "2.12.18", )) lazy val sharedSettings = Def.settings( diff --git a/sbt-plugin/src/sbt-test/settings/legacy-link-empty/build.sbt b/sbt-plugin/src/sbt-test/settings/legacy-link-empty/build.sbt index 3d1087df0d..e8419e778a 100644 --- a/sbt-plugin/src/sbt-test/settings/legacy-link-empty/build.sbt +++ b/sbt-plugin/src/sbt-test/settings/legacy-link-empty/build.sbt @@ -1,4 +1,4 @@ version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/build.sbt b/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/build.sbt index 211f213f25..309e32ab98 100644 --- a/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/build.sbt +++ b/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/build.sbt @@ -1,7 +1,7 @@ val checkNoClosure = taskKey[Unit]("Check that fullOptJS wasn't run with closure") version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/settings/module-init/build.sbt b/sbt-plugin/src/sbt-test/settings/module-init/build.sbt index 9e1176feb8..a70d51266f 100644 --- a/sbt-plugin/src/sbt-test/settings/module-init/build.sbt +++ b/sbt-plugin/src/sbt-test/settings/module-init/build.sbt @@ -3,7 +3,7 @@ import org.scalajs.linker.interface.ModuleInitializer val check = taskKey[Unit]("Run checks of this test") version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/settings/source-map/build.sbt b/sbt-plugin/src/sbt-test/settings/source-map/build.sbt index 7699a258ac..7bfe7a52b6 100644 --- a/sbt-plugin/src/sbt-test/settings/source-map/build.sbt +++ b/sbt-plugin/src/sbt-test/settings/source-map/build.sbt @@ -3,7 +3,7 @@ import org.scalajs.linker.interface.ModuleInitializer val check = taskKey[Unit]("Run checks of this test") version := scalaJSVersion -scalaVersion := "2.12.17" +scalaVersion := "2.12.18" enablePlugins(ScalaJSPlugin) diff --git a/sbt-plugin/src/sbt-test/testing/multi-framework/build.sbt b/sbt-plugin/src/sbt-test/testing/multi-framework/build.sbt index ac4ee6a19f..4b3100395b 100644 --- a/sbt-plugin/src/sbt-test/testing/multi-framework/build.sbt +++ b/sbt-plugin/src/sbt-test/testing/multi-framework/build.sbt @@ -1,5 +1,5 @@ inThisBuild(version := scalaJSVersion) -inThisBuild(scalaVersion := "2.12.17") +inThisBuild(scalaVersion := "2.12.18") lazy val root = project.in(file(".")). aggregate(multiTestJS, multiTestJVM) diff --git a/scala-test-suite/src/test/resources/2.12.18/BlacklistedTests.txt b/scala-test-suite/src/test/resources/2.12.18/BlacklistedTests.txt new file mode 100644 index 0000000000..01525e5e6a --- /dev/null +++ b/scala-test-suite/src/test/resources/2.12.18/BlacklistedTests.txt @@ -0,0 +1,196 @@ +## Do not compile +scala/lang/annotations/BytecodeTest.scala +scala/lang/annotations/RunTest.scala +scala/lang/traits/BytecodeTest.scala +scala/lang/traits/RunTest.scala +scala/lang/primitives/NaNTest.scala +scala/lang/primitives/BoxUnboxTest.scala +scala/collection/SeqTest.scala +scala/collection/Sizes.scala +scala/collection/immutable/HashMapTest.scala +scala/collection/immutable/HashSetTest.scala +scala/collection/immutable/ListMapTest.scala +scala/collection/immutable/MapHashcodeTest.scala +scala/collection/immutable/SetTest.scala +scala/collection/immutable/SeqTest.scala +scala/collection/immutable/SmallMapTest.scala +scala/collection/immutable/SortedMapTest.scala +scala/collection/immutable/SortedSetTest.scala +scala/collection/immutable/TreeMapTest.scala +scala/collection/immutable/TreeSetTest.scala +scala/reflect/ClassOfTest.scala +scala/reflect/QTest.scala +scala/reflect/io/AbstractFileTest.scala +scala/reflect/io/ZipArchiveTest.scala +scala/reflect/internal/util/AbstractFileClassLoaderTest.scala +scala/reflect/internal/util/FileUtilsTest.scala +scala/reflect/internal/util/SourceFileTest.scala +scala/reflect/internal/util/StringOpsTest.scala +scala/reflect/internal/util/WeakHashSetTest.scala +scala/reflect/internal/LongNamesTest.scala +scala/reflect/internal/MirrorsTest.scala +scala/reflect/internal/NamesTest.scala +scala/reflect/internal/PositionsTest.scala +scala/reflect/internal/PrintersTest.scala +scala/reflect/internal/ScopeTest.scala +scala/reflect/internal/TreeGenTest.scala +scala/reflect/internal/TypesTest.scala +scala/reflect/macros/AttachmentsTest.scala +scala/reflect/runtime/ReflectionUtilsShowTest.scala +scala/reflect/runtime/ThreadSafetyTest.scala +scala/runtime/BooleanBoxingTest.scala +scala/runtime/ByteBoxingTest.scala +scala/runtime/CharBoxingTest.scala +scala/runtime/DoubleBoxingTest.scala +scala/runtime/IntBoxingTest.scala +scala/runtime/FloatBoxingTest.scala +scala/runtime/LongBoxingTest.scala +scala/runtime/ShortBoxingTest.scala +scala/tools/cmd/CommandLineParserTest.scala +scala/tools/nsc/Build.scala +scala/tools/nsc/DeterminismTest.scala +scala/tools/nsc/DeterminismTester.scala +scala/tools/nsc/FileUtils.scala +scala/tools/nsc/GlobalCustomizeClassloaderTest.scala +scala/tools/nsc/PickleWriteTest.scala +scala/tools/nsc/PipelineMainTest.scala +scala/tools/nsc/async/AnnotationDrivenAsync.scala +scala/tools/nsc/async/CustomFuture.scala +scala/tools/nsc/backend/jvm/BTypesTest.scala +scala/tools/nsc/backend/jvm/BytecodeTest.scala +scala/tools/nsc/backend/jvm/DefaultMethodTest.scala +scala/tools/nsc/backend/jvm/DirectCompileTest.scala +scala/tools/nsc/backend/jvm/GenericSignaturesTest.scala +scala/tools/nsc/backend/jvm/IndyLambdaDirectTest.scala +scala/tools/nsc/backend/jvm/IndyLambdaTest.scala +scala/tools/nsc/backend/jvm/IndySammyTest.scala +scala/tools/nsc/backend/jvm/InnerClassAttributeTest.scala +scala/tools/nsc/backend/jvm/LineNumberTest.scala +scala/tools/nsc/backend/jvm/NestedClassesCollectorTest.scala +scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala +scala/tools/nsc/backend/jvm/PerRunInitTest.scala +scala/tools/nsc/backend/jvm/StringConcatTest.scala +scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala +scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala +scala/tools/nsc/backend/jvm/opt/AnalyzerTest.scala +scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala +scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala +scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala +scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala +scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala +scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala +scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala +scala/tools/nsc/backend/jvm/opt/EmptyLabelsAndLineNumbersTest.scala +scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala +scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala +scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala +scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +scala/tools/nsc/backend/jvm/opt/InlineSourceMatcherTest.scala +scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala +scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala +scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala +scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala +scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala +scala/tools/nsc/ScriptRunnerTest.scala +scala/tools/nsc/classpath/AggregateClassPathTest.scala +scala/tools/nsc/classpath/JrtClassPathTest.scala +scala/tools/nsc/classpath/MultiReleaseJarTest.scala +scala/tools/nsc/classpath/PathResolverBaseTest.scala +scala/tools/nsc/classpath/VirtualDirectoryClassPathTest.scala +scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala +scala/tools/nsc/doc/html/HtmlDocletTest.scala +scala/tools/nsc/interpreter/CompletionTest.scala +scala/tools/nsc/interpreter/ScriptedTest.scala +scala/tools/nsc/interpreter/TabulatorTest.scala +scala/tools/nsc/parser/ParserTest.scala +scala/tools/nsc/reporters/ConsoleReporterTest.scala +scala/tools/nsc/reporters/WConfTest.scala +scala/tools/nsc/settings/ScalaVersionTest.scala +scala/tools/nsc/settings/SettingsTest.scala +scala/tools/nsc/settings/TargetTest.scala +scala/tools/nsc/symtab/CannotHaveAttrsTest.scala +scala/tools/nsc/symtab/FlagsTest.scala +scala/tools/nsc/symtab/FreshNameExtractorTest.scala +scala/tools/nsc/symtab/StdNamesTest.scala +scala/tools/nsc/symtab/SymbolLoadersAssociatedFileTest.scala +scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala +scala/tools/nsc/symtab/SymbolTableTest.scala +scala/tools/nsc/symtab/classfile/PicklerTest.scala +scala/tools/nsc/transform/MixinTest.scala +scala/tools/nsc/transform/SpecializationTest.scala +scala/tools/nsc/transform/ThicketTransformerTest.scala +scala/tools/nsc/transform/delambdafy/DelambdafyTest.scala +scala/tools/nsc/transform/patmat/SolvingTest.scala +scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala +scala/tools/nsc/typechecker/Implicits.scala +scala/tools/nsc/typechecker/NamerTest.scala +scala/tools/nsc/typechecker/ParamAliasTest.scala +scala/tools/nsc/typechecker/TreeAttachmentTest.scala +scala/tools/nsc/typechecker/TypedTreeTest.scala +scala/tools/nsc/util/StackTraceTest.scala +scala/tools/testing/AllocationTest.scala +scala/tools/testing/BytecodeTesting.scala +scala/tools/testing/JOL.scala +scala/tools/testing/RunTesting.scala +scala/tools/testing/VirtualCompilerTesting.scala +scala/util/matching/RegexTest.scala + +## Do not link +scala/MatchErrorSerializationTest.scala +scala/PartialFunctionSerializationTest.scala +scala/lang/stringinterpol/StringContextTest.scala +scala/collection/IteratorTest.scala +scala/collection/NewBuilderTest.scala +scala/collection/ParallelConsistencyTest.scala +scala/collection/SetMapRulesTest.scala +scala/collection/SeqViewTest.scala +scala/collection/SetMapConsistencyTest.scala +scala/collection/concurrent/TrieMapTest.scala +scala/collection/convert/WrapperSerializationTest.scala +scala/collection/immutable/ListTest.scala +scala/collection/immutable/RedBlackTreeSerialFormat.scala +scala/collection/immutable/StreamTest.scala +scala/collection/immutable/StringLikeTest.scala +scala/collection/immutable/VectorTest.scala +scala/collection/mutable/AnyRefMapTest.scala +scala/collection/mutable/ArrayBufferTest.scala +scala/collection/mutable/MutableListTest.scala +scala/collection/mutable/OpenHashMapTest.scala +scala/collection/mutable/PriorityQueueTest.scala +scala/collection/parallel/TaskTest.scala +scala/collection/parallel/immutable/ParRangeTest.scala +scala/concurrent/FutureTest.scala +scala/concurrent/duration/SerializationTest.scala +scala/concurrent/impl/DefaultPromiseTest.scala +scala/io/SourceTest.scala +scala/runtime/ScalaRunTimeTest.scala +scala/sys/process/PipedProcessTest.scala +scala/sys/process/ProcessTest.scala +scala/tools/testing/AssertUtilTest.scala +scala/tools/testing/AssertThrowsTest.scala +scala/util/SpecVersionTest.scala +scala/util/SystemPropertiesTest.scala + +## Tests fail + +# Reflection +scala/reflect/ClassTagTest.scala + +# Require strict-floats +scala/math/BigDecimalTest.scala + +# Difference of getClass() on primitive values +scala/collection/immutable/RangeTest.scala + +# Test fails only some times with +# 'set scalaJSOptimizerOptions in scalaTestSuite ~= (_.withDisableOptimizer(true))' +# and' 'set scalaJSUseRhino in Global := false' +scala/collection/immutable/PagedSeqTest.scala + +# Bugs +scala/collection/convert/MapWrapperTest.scala + +# Tests passed but are too slow (timeouts) +scala/collection/immutable/ListSetTest.scala +scala/util/SortingTest.scala From aeb9d1619aa85d997ea115636e1056c35b378fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 5 Jun 2023 13:32:44 +0200 Subject: [PATCH 22/27] Port fixes to NumericRange.{take,drop}. Port of the upstream commit https://github.com/scala/scala/commit/61557d64f8ead0026ab3744d707adf13fa0465f3 --- .../collection/immutable/NumericRange.scala | 73 ++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/scalalib/overrides-2.13/scala/collection/immutable/NumericRange.scala b/scalalib/overrides-2.13/scala/collection/immutable/NumericRange.scala index 3ac177afcc..c12e85e5e3 100644 --- a/scalalib/overrides-2.13/scala/collection/immutable/NumericRange.scala +++ b/scalalib/overrides-2.13/scala/collection/immutable/NumericRange.scala @@ -132,6 +132,75 @@ sealed class NumericRange[T]( // are forgiving: therefore the checks are with the methods. private def locationAfterN(n: Int): T = start + (step * fromInt(n)) + private def crossesTheEndAfterN(n: Int): Boolean = { + // if we're sure that subtraction in the context of T won't overflow, we use this function + // to calculate the length of the range + def unsafeRangeLength(r: NumericRange[T]): T = { + val diff = num.minus(r.end, r.start) + val quotient = num.quot(diff, r.step) + val remainder = num.rem(diff, r.step) + if (!r.isInclusive && num.equiv(remainder, num.zero)) + num.max(quotient, num.zero) + else + num.max(num.plus(quotient, num.one), num.zero) + } + + // detects whether value can survive a bidirectional trip to -and then from- Int. + def fitsInInteger(value: T): Boolean = num.equiv(num.fromInt(num.toInt(value)), value) + + val stepIsInTheSameDirectionAsStartToEndVector = + (num.gt(end, start) && num.gt(step, num.zero)) || (num.lt(end, start) && num.sign(step) == -num.one) + + if (num.equiv(start, end) || n <= 0 || !stepIsInTheSameDirectionAsStartToEndVector) return n >= 1 + + val sameSign = num.equiv(num.sign(start), num.sign(end)) + + if (sameSign) { // subtraction is safe + val len = unsafeRangeLength(this) + if (fitsInInteger(len)) n >= num.toInt(len) else num.gteq(num.fromInt(n), len) + } else { + // split to two ranges, which subtraction is safe in both of them (around zero) + val stepsRemainderToZero = num.rem(start, step) + val walksOnZero = num.equiv(stepsRemainderToZero, num.zero) + val closestToZero = if (walksOnZero) -step else stepsRemainderToZero + + /* + When splitting into two ranges, we should be super-careful about one of the sides hitting MinValue of T, + so we take two steps smaller than zero to ensure unsafeRangeLength won't overflow (taking one step may overflow depending on the step). + Same thing happens for MaxValue from zero, so we take one step further to ensure the safety of unsafeRangeLength. + After performing such operation, there are some elements remaining in between and around zero, + which their length is represented by carry. + */ + val (l: NumericRange[T], r: NumericRange[T], carry: Int) = + if (num.lt(start, num.zero)) { + if (walksOnZero) { + val twoStepsAfterLargestNegativeNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (NumericRange(start, closestToZero, step), copy(twoStepsAfterLargestNegativeNumber, end, step), 2) + } else { + (NumericRange(start, closestToZero, step), copy(num.plus(closestToZero, step), end, step), 1) + } + } else { + if (walksOnZero) { + val twoStepsAfterZero = num.times(step, num.fromInt(2)) + (copy(twoStepsAfterZero, end, step), NumericRange.inclusive(start, -step, step), 2) + } else { + val twoStepsAfterSmallestPositiveNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (copy(twoStepsAfterSmallestPositiveNumber, end, step), NumericRange.inclusive(start, closestToZero, step), 2) + } + } + + val leftLength = unsafeRangeLength(l) + val rightLength = unsafeRangeLength(r) + + // instead of `n >= rightLength + leftLength + curry` which may cause addition overflow, + // this can be used `(n - leftLength - curry) >= rightLength` (Both in Int and T, depends on whether the lengths fit in Int) + if (fitsInInteger(leftLength) && fitsInInteger(rightLength)) + n - num.toInt(leftLength) - carry >= num.toInt(rightLength) + else + num.gteq(num.minus(num.minus(num.fromInt(n), leftLength), num.fromInt(carry)), rightLength) + } + } + // When one drops everything. Can't ever have unchecked operations // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } // will overflow. This creates an exclusive range where start == end @@ -140,13 +209,13 @@ sealed class NumericRange[T]( override def take(n: Int): NumericRange[T] = { if (n <= 0 || isEmpty) newEmptyRange(start) - else if (n >= length) this + else if (crossesTheEndAfterN(n)) this else new NumericRange.Inclusive(start, locationAfterN(n - 1), step) } override def drop(n: Int): NumericRange[T] = { if (n <= 0 || isEmpty) this - else if (n >= length) newEmptyRange(end) + else if (crossesTheEndAfterN(n)) newEmptyRange(end) else copy(locationAfterN(n), end, step) } From fae8bf1c16fc6aa3e87a212fa91d8280e22d786e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 5 Jun 2023 14:30:33 +0200 Subject: [PATCH 23/27] Upgrade to Scala 2.13.11. The code size increase is due to changes in `Vector.scala`. --- Jenkinsfile | 5 +- .../{2.13.10 => 2.13.11}/BlacklistedTests.txt | 10 + .../{2.13.10 => 2.13.11}/neg/choices.check | 0 .../neg/partestInvalidFlag.check | 0 .../{2.13.10 => 2.13.11}/neg/t11952b.check | 0 .../partest/scalajs/2.13.11/neg/t12494.check | 166 +++++++++++++ .../neg/t6446-additional.check | 0 .../{2.13.10 => 2.13.11}/neg/t6446-list.check | 0 .../neg/t6446-missing.check | 0 .../neg/t6446-show-phases.check | 0 .../neg/t7494-no-options.check | 0 .../run/Course-2002-01.check | 0 .../run/Course-2002-02.check | 0 .../run/Course-2002-04.check | 0 .../run/Course-2002-08.check | 0 .../run/Course-2002-09.check | 0 .../run/Course-2002-10.check | 0 .../{2.13.10 => 2.13.11}/run/Meter.check | 0 .../run/MeterCaseClass.check | 0 .../run/anyval-box-types.check | 0 .../scalajs/{2.13.10 => 2.13.11}/run/bugs.sem | 0 .../run/caseClassHash.check | 0 .../{2.13.10 => 2.13.11}/run/classof.check | 0 .../{2.13.10 => 2.13.11}/run/deeps.check | 0 .../run/dynamic-anyval.check | 0 .../{2.13.10 => 2.13.11}/run/exceptions-2.sem | 0 .../run/exceptions-nest.check | 0 .../run/exceptions-nest.sem | 0 .../run/impconvtimes.check | 0 .../{2.13.10 => 2.13.11}/run/imports.check | 0 .../run/inlineHandlers.sem | 0 .../run/interpolation.check | 0 .../run/interpolationMultiline1.check | 0 .../run/macro-bundle-static.check | 0 .../run/macro-bundle-toplevel.check | 0 .../run/macro-bundle-whitebox-decl.check | 0 ...expand-varargs-implicit-over-varargs.check | 0 .../{2.13.10 => 2.13.11}/run/misc.check | 0 .../run/optimizer-array-load.sem | 0 .../{2.13.10 => 2.13.11}/run/pf-catch.sem | 0 .../{2.13.10 => 2.13.11}/run/promotion.check | 0 .../{2.13.10 => 2.13.11}/run/runtime.check | 0 .../run/sammy_vararg_cbn.check | 0 .../{2.13.10 => 2.13.11}/run/spec-self.check | 0 .../run/string-switch.check | 0 .../{2.13.10 => 2.13.11}/run/structural.check | 0 .../{2.13.10 => 2.13.11}/run/t0421-new.check | 0 .../{2.13.10 => 2.13.11}/run/t0421-old.check | 0 .../{2.13.10 => 2.13.11}/run/t12221.check | 0 .../{2.13.10 => 2.13.11}/run/t1503.sem | 0 .../{2.13.10 => 2.13.11}/run/t3702.check | 0 .../{2.13.10 => 2.13.11}/run/t4148.sem | 0 .../{2.13.10 => 2.13.11}/run/t4617.check | 0 .../{2.13.10 => 2.13.11}/run/t5356.check | 0 .../{2.13.10 => 2.13.11}/run/t5552.check | 0 .../{2.13.10 => 2.13.11}/run/t5568.check | 0 .../{2.13.10 => 2.13.11}/run/t5629b.check | 0 .../{2.13.10 => 2.13.11}/run/t5680.check | 0 .../{2.13.10 => 2.13.11}/run/t5866.check | 0 .../{2.13.10 => 2.13.11}/run/t5966.check | 0 .../{2.13.10 => 2.13.11}/run/t6265.check | 0 .../run/t6318_primitives.check | 0 .../{2.13.10 => 2.13.11}/run/t6662.check | 0 .../{2.13.10 => 2.13.11}/run/t6827.sem | 0 .../{2.13.10 => 2.13.11}/run/t7657.check | 0 .../{2.13.10 => 2.13.11}/run/t7763.sem | 0 .../{2.13.10 => 2.13.11}/run/t8570a.check | 0 .../{2.13.10 => 2.13.11}/run/t8601b.sem | 0 .../{2.13.10 => 2.13.11}/run/t8601c.sem | 0 .../{2.13.10 => 2.13.11}/run/t8601d.sem | 0 .../{2.13.10 => 2.13.11}/run/t8764.check | 0 .../{2.13.10 => 2.13.11}/run/t9387b.check | 0 .../run/try-catch-unify.check | 0 .../run/virtpatmat_switch.check | 0 .../run/virtpatmat_typetag.check | 0 project/Build.scala | 9 +- .../src/sbt-test/cross-version/2.13/build.sbt | 2 +- .../sbt-test/scala3/tasty-reader/build.sbt | 2 +- .../resources/2.13.11/BlacklistedTests.txt | 226 ++++++++++++++++++ .../scalajs/testing/adapter/TestAdapter.scala | 2 +- 80 files changed, 413 insertions(+), 9 deletions(-) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/BlacklistedTests.txt (99%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/choices.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/partestInvalidFlag.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/t11952b.check (100%) create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t12494.check rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/t6446-additional.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/t6446-list.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/t6446-missing.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/t6446-show-phases.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/neg/t7494-no-options.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Course-2002-01.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Course-2002-02.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Course-2002-04.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Course-2002-08.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Course-2002-09.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Course-2002-10.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/Meter.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/MeterCaseClass.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/anyval-box-types.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/bugs.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/caseClassHash.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/classof.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/deeps.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/dynamic-anyval.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/exceptions-2.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/exceptions-nest.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/exceptions-nest.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/impconvtimes.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/imports.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/inlineHandlers.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/interpolation.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/interpolationMultiline1.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/macro-bundle-static.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/macro-bundle-toplevel.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/macro-bundle-whitebox-decl.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/macro-expand-varargs-implicit-over-varargs.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/misc.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/optimizer-array-load.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/pf-catch.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/promotion.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/runtime.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/sammy_vararg_cbn.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/spec-self.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/string-switch.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/structural.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t0421-new.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t0421-old.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t12221.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t1503.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t3702.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t4148.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t4617.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5356.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5552.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5568.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5629b.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5680.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5866.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t5966.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t6265.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t6318_primitives.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t6662.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t6827.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t7657.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t7763.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t8570a.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t8601b.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t8601c.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t8601d.sem (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t8764.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/t9387b.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/try-catch-unify.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/virtpatmat_switch.check (100%) rename partest-suite/src/test/resources/scala/tools/partest/scalajs/{2.13.10 => 2.13.11}/run/virtpatmat_typetag.check (100%) create mode 100644 scala-test-suite/src/test/resources/2.13.11/BlacklistedTests.txt diff --git a/Jenkinsfile b/Jenkinsfile index ae5e56ca16..03e477a676 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -453,7 +453,7 @@ def allJavaVersions = otherJavaVersions.clone() allJavaVersions << mainJavaVersion def mainScalaVersion = "2.12.18" -def mainScalaVersions = ["2.12.18", "2.13.10"] +def mainScalaVersions = ["2.12.18", "2.13.11"] def otherScalaVersions = [ "2.12.2", "2.12.3", @@ -479,7 +479,8 @@ def otherScalaVersions = [ "2.13.6", "2.13.7", "2.13.8", - "2.13.9" + "2.13.9", + "2.13.10" ] def scala3Version = "3.2.1" diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/BlacklistedTests.txt similarity index 99% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/BlacklistedTests.txt index cd03f2194a..f34db1bec3 100644 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/BlacklistedTests.txt @@ -635,6 +635,11 @@ run/module-static.scala run/module-serialization-proxy-class-unload.scala run/t9644.scala +run/dotty-i11332.scala +run/dotty-i11332b.scala +run/dotty-t12348.scala +run/t12348.scala + # Uses reflection indirectly through # scala.runtime.ScalaRunTime.replStringOf run/t6634.scala @@ -818,6 +823,10 @@ run/interpolation-repl.scala run/t12292.scala run/StringConcat.scala run/t10016.scala +run/t12705.scala +run/t12390.scala +run/repl-release.scala +run/eta-dependent.scala # Using Scala Script (partest.ScriptTest) @@ -1101,6 +1110,7 @@ run/t12038b run/t12195 run/t12380 run/t12523 +run/t12290 # Using scala-script run/t7791-script-linenums.scala diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/choices.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/choices.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/choices.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/choices.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/partestInvalidFlag.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/partestInvalidFlag.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/partestInvalidFlag.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/partestInvalidFlag.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t11952b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t11952b.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t11952b.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t11952b.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t12494.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t12494.check new file mode 100644 index 0000000000..0d261fc135 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t12494.check @@ -0,0 +1,166 @@ +[running phase parser on t12494.scala] +[running phase jspretyper on t12494.scala] +[running phase namer on t12494.scala] +[running phase packageobjects on t12494.scala] +[running phase typer on t12494.scala] +[running phase jsinterop on t12494.scala] +[running phase superaccessors on t12494.scala] +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / EmptyTree +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / Ident() +[log superaccessors] [context] ++ t12494.scala / Ident() +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term m +[log superaccessors] [context] ++ t12494.scala / term m +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term Y +[log superaccessors] [context] ++ t12494.scala / term Y +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term n +[log superaccessors] [context] ++ t12494.scala / term n +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / type X +[log superaccessors] [context] ++ t12494.scala / type X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term x +[log superaccessors] [context] ++ t12494.scala / term x +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] In trait Base, renaming g -> Base$$g +[log superaccessors] Expanded 'g' to 'Base$$g' in trait Base +[log superaccessors] In trait Base, renaming h -> Base$$h +[log superaccessors] Expanded 'h' to 'Base$$h' in trait Base +[log superaccessors] In trait Base, renaming p -> Base$$p +[log superaccessors] Expanded 'p' to 'Base$$p' in trait Base +[log superaccessors] [context] ++ t12494.scala / type Base +[log superaccessors] [context] ++ t12494.scala / type Base +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term Base +[log superaccessors] [context] ++ t12494.scala / term Base +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type Child +[log superaccessors] [context] ++ t12494.scala / type Child +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term p +[running phase extmethods on t12494.scala] +[running phase pickler on t12494.scala] +[running phase refchecks on t12494.scala] +t12494.scala:9: error: weaker access privileges in overriding + protected[trait C] def f: scala.this.Int (defined in trait C) + override should at least be protected[C]; + found : scala.this.Int + required: scala.this.Int + protected[C] def f: Int = 42 // no, limitation + ^ +t12494.scala:28: error: weaker access privileges in overriding + protected[trait C] def f: scala.this.Int (defined in trait C) + override should at least be protected[C]; + found : scala.this.Int + required: scala.this.Int + protected[C] def f: Int = 42 // no + ^ +t12494.scala:47: error: class Child needs to be abstract. +Missing implementations for 3 members of trait Base. + private[trait Base] def g: scala.this.Int = ??? + private[trait Base] def h: scala.this.Int = ??? + private[trait Base] def p: scala.this.Int = ??? + + class Child extends Base { + ^ +t12494.scala:50: error: method g overrides nothing + override private[Base] def g: Int = 42 // ok, companion + ^ +t12494.scala:51: error: method h overrides nothing + override protected[Base] def h: Int = 42 // ok, private[C] widens to protected[C] + ^ +t12494.scala:52: error: method p overrides nothing + override protected def p: Int = 42 // error, protected only overrides protected + ^ +6 errors diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-additional.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-additional.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-additional.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-additional.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-list.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-list.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-list.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-list.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-missing.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-missing.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-missing.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-missing.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-show-phases.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-show-phases.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t6446-show-phases.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t6446-show-phases.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t7494-no-options.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t7494-no-options.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/neg/t7494-no-options.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/neg/t7494-no-options.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-01.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-01.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-01.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-01.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-02.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-02.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-02.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-02.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-04.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-04.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-04.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-04.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-08.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-08.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-08.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-08.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-09.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-09.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-09.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-09.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-10.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-10.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Course-2002-10.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Course-2002-10.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Meter.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Meter.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/Meter.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/Meter.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/MeterCaseClass.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/MeterCaseClass.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/MeterCaseClass.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/MeterCaseClass.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/anyval-box-types.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/anyval-box-types.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/anyval-box-types.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/anyval-box-types.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/bugs.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/bugs.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/bugs.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/bugs.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/caseClassHash.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/caseClassHash.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/caseClassHash.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/caseClassHash.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/classof.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/classof.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/classof.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/classof.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/deeps.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/deeps.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/deeps.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/deeps.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/dynamic-anyval.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/dynamic-anyval.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/dynamic-anyval.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/dynamic-anyval.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-2.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/exceptions-2.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-2.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/exceptions-2.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/exceptions-nest.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/exceptions-nest.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/exceptions-nest.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/exceptions-nest.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/impconvtimes.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/impconvtimes.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/impconvtimes.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/impconvtimes.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/imports.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/imports.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/imports.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/imports.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/inlineHandlers.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/inlineHandlers.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/inlineHandlers.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/inlineHandlers.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/interpolation.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/interpolation.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/interpolation.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/interpolation.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/interpolationMultiline1.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/interpolationMultiline1.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/interpolationMultiline1.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/interpolationMultiline1.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-bundle-static.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-bundle-static.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-bundle-static.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-bundle-static.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-bundle-toplevel.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-bundle-toplevel.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-bundle-toplevel.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-bundle-toplevel.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-bundle-whitebox-decl.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-bundle-whitebox-decl.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-bundle-whitebox-decl.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-bundle-whitebox-decl.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-expand-varargs-implicit-over-varargs.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-expand-varargs-implicit-over-varargs.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/macro-expand-varargs-implicit-over-varargs.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/macro-expand-varargs-implicit-over-varargs.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/misc.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/misc.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/misc.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/misc.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/optimizer-array-load.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/optimizer-array-load.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/optimizer-array-load.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/optimizer-array-load.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/pf-catch.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/pf-catch.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/pf-catch.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/pf-catch.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/promotion.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/promotion.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/promotion.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/promotion.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/runtime.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/runtime.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/runtime.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/runtime.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/sammy_vararg_cbn.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/sammy_vararg_cbn.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/sammy_vararg_cbn.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/sammy_vararg_cbn.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/spec-self.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/spec-self.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/spec-self.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/spec-self.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/string-switch.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/string-switch.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/string-switch.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/string-switch.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/structural.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/structural.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/structural.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/structural.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t0421-new.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t0421-new.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t0421-new.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t0421-new.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t0421-old.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t0421-old.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t0421-old.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t0421-old.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t12221.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t12221.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t12221.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t12221.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t1503.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t1503.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t1503.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t1503.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t3702.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t3702.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t3702.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t3702.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t4148.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t4148.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t4148.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t4148.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t4617.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t4617.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t4617.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t4617.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5356.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5356.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5356.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5356.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5552.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5552.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5552.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5552.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5568.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5568.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5568.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5568.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5629b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5629b.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5629b.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5629b.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5680.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5680.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5680.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5680.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5866.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5866.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5866.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5866.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5966.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5966.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t5966.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t5966.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6265.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6265.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6265.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6265.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6318_primitives.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6318_primitives.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6318_primitives.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6318_primitives.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6662.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6662.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6662.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6662.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6827.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6827.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6827.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t6827.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t7657.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t7657.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t7657.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t7657.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t7763.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t7763.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t7763.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t7763.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8570a.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8570a.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8570a.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8570a.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601b.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8601b.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601b.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8601b.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601c.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8601c.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601c.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8601c.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601d.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8601d.sem similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601d.sem rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8601d.sem diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8764.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8764.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8764.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t8764.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t9387b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t9387b.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t9387b.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/t9387b.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/try-catch-unify.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/try-catch-unify.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/try-catch-unify.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/try-catch-unify.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/virtpatmat_switch.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/virtpatmat_switch.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/virtpatmat_switch.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/virtpatmat_switch.check diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/virtpatmat_typetag.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/virtpatmat_typetag.check similarity index 100% rename from partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/virtpatmat_typetag.check rename to partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.11/run/virtpatmat_typetag.check diff --git a/project/Build.scala b/project/Build.scala index 5d6664688a..4dbeba6445 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -816,6 +816,7 @@ object Build { "2.13.8", "2.13.9", "2.13.10", + "2.13.11", ), default212ScalaVersion := cross212ScalaVersions.value.last, @@ -1858,10 +1859,10 @@ object Build { case `default213Version` => Some(ExpectedSizes( - fastLink = 456000 to 457000, - fullLink = 97000 to 98000, - fastLinkGz = 59000 to 60000, - fullLinkGz = 26000 to 27000, + fastLink = 480000 to 481000, + fullLink = 102000 to 103000, + fastLinkGz = 62000 to 63000, + fullLinkGz = 27000 to 28000, )) case _ => diff --git a/sbt-plugin/src/sbt-test/cross-version/2.13/build.sbt b/sbt-plugin/src/sbt-test/cross-version/2.13/build.sbt index 821db9a586..d68a98fa30 100644 --- a/sbt-plugin/src/sbt-test/cross-version/2.13/build.sbt +++ b/sbt-plugin/src/sbt-test/cross-version/2.13/build.sbt @@ -2,6 +2,6 @@ enablePlugins(ScalaJSPlugin) enablePlugins(ScalaJSJUnitPlugin) version := scalaJSVersion -scalaVersion := "2.13.10" +scalaVersion := "2.13.11" scalaJSUseMainModuleInitializer := true diff --git a/sbt-plugin/src/sbt-test/scala3/tasty-reader/build.sbt b/sbt-plugin/src/sbt-test/scala3/tasty-reader/build.sbt index 2e12aebca0..d761be027d 100644 --- a/sbt-plugin/src/sbt-test/scala3/tasty-reader/build.sbt +++ b/sbt-plugin/src/sbt-test/scala3/tasty-reader/build.sbt @@ -10,7 +10,7 @@ lazy val app = project.in(file("app")) .enablePlugins(ScalaJSPlugin) .dependsOn(testlib) .settings( - scalaVersion := "2.13.10", + scalaVersion := "2.13.11", scalacOptions += "-Ytasty-reader", scalaJSUseMainModuleInitializer := true ) diff --git a/scala-test-suite/src/test/resources/2.13.11/BlacklistedTests.txt b/scala-test-suite/src/test/resources/2.13.11/BlacklistedTests.txt new file mode 100644 index 0000000000..bd9d42d165 --- /dev/null +++ b/scala-test-suite/src/test/resources/2.13.11/BlacklistedTests.txt @@ -0,0 +1,226 @@ +## Do not compile +scala/ExtractorTest.scala +scala/OptionTest.scala +scala/SerializationStabilityTest.scala +scala/StringTest.scala +scala/collection/FactoriesTest.scala +scala/collection/LazyZipOpsTest.scala +scala/collection/SeqTest.scala +scala/collection/immutable/HashMapTest.scala +scala/collection/immutable/HashSetTest.scala +scala/collection/immutable/IndexedSeqTest.scala +scala/collection/immutable/IntMapTest.scala +scala/collection/immutable/LazyListTest.scala +scala/collection/immutable/ListMapTest.scala +scala/collection/immutable/LongMapTest.scala +scala/collection/immutable/MapHashcodeTest.scala +scala/collection/immutable/SeqTest.scala +scala/collection/immutable/SmallMapTest.scala +scala/collection/immutable/SortedMapTest.scala +scala/collection/immutable/SortedSetTest.scala +scala/collection/immutable/TreeMapTest.scala +scala/collection/immutable/TreeSetTest.scala +scala/lang/annotations/BytecodeTest.scala +scala/lang/annotations/RunTest.scala +scala/lang/traits/BytecodeTest.scala +scala/lang/traits/RunTest.scala +scala/lang/primitives/NaNTest.scala +scala/reflect/ClassOfTest.scala +scala/reflect/FieldAccessTest.scala +scala/reflect/QTest.scala +scala/reflect/macros/AttachmentsTest.scala +scala/reflect/io/ZipArchiveTest.scala +scala/reflect/internal/InferTest.scala +scala/reflect/internal/LongNamesTest.scala +scala/reflect/internal/MirrorsTest.scala +scala/reflect/internal/NamesTest.scala +scala/reflect/internal/PositionsTest.scala +scala/reflect/internal/PrintersTest.scala +scala/reflect/internal/ScopeTest.scala +scala/reflect/internal/SubstMapTest.scala +scala/reflect/internal/TreeGenTest.scala +scala/reflect/internal/TypesTest.scala +scala/reflect/internal/util/AbstractFileClassLoaderTest.scala +scala/reflect/internal/util/FileUtilsTest.scala +scala/reflect/internal/util/SourceFileTest.scala +scala/reflect/internal/util/StringOpsTest.scala +scala/reflect/internal/util/WeakHashSetTest.scala +scala/reflect/io/AbstractFileTest.scala +scala/reflect/runtime/ReflectionUtilsShowTest.scala +scala/reflect/runtime/ThreadSafetyTest.scala +scala/runtime/BooleanBoxingTest.scala +scala/runtime/ByteBoxingTest.scala +scala/runtime/CharBoxingTest.scala +scala/runtime/DoubleBoxingTest.scala +scala/runtime/IntBoxingTest.scala +scala/runtime/FloatBoxingTest.scala +scala/runtime/LongBoxingTest.scala +scala/runtime/ShortBoxingTest.scala +scala/tools/nsc/Build.scala +scala/tools/nsc/DeterminismTest.scala +scala/tools/nsc/DeterminismTester.scala +scala/tools/nsc/FileUtils.scala +scala/tools/nsc/GlobalCustomizeClassloaderTest.scala +scala/tools/nsc/MainRunnerTest.scala +scala/tools/nsc/PhaseAssemblyTest.scala +scala/tools/nsc/PickleWriteTest.scala +scala/tools/nsc/PipelineMainTest.scala +scala/tools/nsc/ScriptRunnerTest.scala +scala/tools/nsc/async/AnnotationDrivenAsyncTest.scala +scala/tools/nsc/async/CustomFuture.scala +scala/tools/nsc/backend/jvm/BTypesTest.scala +scala/tools/nsc/backend/jvm/BytecodeTest.scala +scala/tools/nsc/backend/jvm/DefaultMethodTest.scala +scala/tools/nsc/backend/jvm/DirectCompileTest.scala +scala/tools/nsc/backend/jvm/GenericSignaturesTest.scala +scala/tools/nsc/backend/jvm/IndyLambdaTest.scala +scala/tools/nsc/backend/jvm/IndySammyTest.scala +scala/tools/nsc/backend/jvm/InnerClassAttributeTest.scala +scala/tools/nsc/backend/jvm/LineNumberTest.scala +scala/tools/nsc/backend/jvm/NestedClassesCollectorTest.scala +scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala +scala/tools/nsc/backend/jvm/PerRunInitTest.scala +scala/tools/nsc/backend/jvm/StringConcatTest.scala +scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala +scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala +scala/tools/nsc/backend/jvm/analysis/TypeFlowAnalyzerTest.scala +scala/tools/nsc/backend/jvm/opt/AnalyzerTest.scala +scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala +scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala +scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala +scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala +scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala +scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala +scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala +scala/tools/nsc/backend/jvm/opt/EmptyLabelsAndLineNumbersTest.scala +scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala +scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala +scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala +scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +scala/tools/nsc/backend/jvm/opt/InlineSourceMatcherTest.scala +scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala +scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala +scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala +scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala +scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala +scala/tools/nsc/classpath/AggregateClassPathTest.scala +scala/tools/nsc/classpath/JrtClassPathTest.scala +scala/tools/nsc/classpath/MultiReleaseJarTest.scala +scala/tools/nsc/classpath/PathResolverBaseTest.scala +scala/tools/nsc/classpath/VirtualDirectoryClassPathTest.scala +scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala +scala/tools/nsc/doc/html/HtmlDocletTest.scala +scala/tools/nsc/doc/html/StringLiteralTest.scala +scala/tools/nsc/interpreter/CompletionTest.scala +scala/tools/nsc/interpreter/ScriptedTest.scala +scala/tools/nsc/interpreter/TabulatorTest.scala +scala/tools/nsc/parser/ParserTest.scala +scala/tools/nsc/reporters/ConsoleReporterTest.scala +scala/tools/nsc/reporters/PositionFilterTest.scala +scala/tools/nsc/reporters/WConfTest.scala +scala/tools/nsc/settings/ScalaVersionTest.scala +scala/tools/nsc/settings/SettingsTest.scala +scala/tools/nsc/settings/TargetTest.scala +scala/tools/nsc/symtab/CannotHaveAttrsTest.scala +scala/tools/nsc/symtab/FlagsTest.scala +scala/tools/nsc/symtab/FreshNameExtractorTest.scala +scala/tools/nsc/symtab/StdNamesTest.scala +scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala +scala/tools/nsc/symtab/SymbolTableTest.scala +scala/tools/nsc/symtab/classfile/PicklerTest.scala +scala/tools/nsc/transform/ErasureTest.scala +scala/tools/nsc/transform/MixinTest.scala +scala/tools/nsc/transform/ReleaseFenceTest.scala +scala/tools/nsc/transform/SpecializationTest.scala +scala/tools/nsc/transform/ThicketTransformerTest.scala +scala/tools/nsc/transform/UncurryTest.scala +scala/tools/nsc/transform/delambdafy/DelambdafyTest.scala +scala/tools/nsc/transform/patmat/SolvingTest.scala +scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala +scala/tools/nsc/typechecker/ConstantFolderTest.scala +scala/tools/nsc/typechecker/ImplicitsTest.scala +scala/tools/nsc/typechecker/InferencerTest.scala +scala/tools/nsc/typechecker/NamerTest.scala +scala/tools/nsc/typechecker/OverridingPairsTest.scala +scala/tools/nsc/typechecker/ParamAliasTest.scala +scala/tools/nsc/typechecker/TreeAttachmentTest.scala +scala/tools/nsc/typechecker/TypedTreeTest.scala +scala/tools/nsc/util/StackTraceTest.scala +scala/tools/testkit/ReflectUtilTest.scala +scala/util/ChainingOpsTest.scala + +## Do not link +scala/CollectTest.scala +scala/MatchErrorSerializationTest.scala +scala/PartialFunctionSerializationTest.scala +scala/lang/stringinterpol/StringContextTest.scala +scala/collection/IterableTest.scala +scala/collection/IteratorTest.scala +scala/collection/NewBuilderTest.scala +scala/collection/SeqViewTest.scala +scala/collection/SetMapConsistencyTest.scala +scala/collection/SetMapRulesTest.scala +scala/collection/Sizes.scala +scala/collection/ViewTest.scala +scala/collection/concurrent/TrieMapTest.scala +scala/collection/convert/JConcurrentMapWrapperTest.scala +scala/collection/convert/WrapperSerializationTest.scala +scala/collection/immutable/ChampMapSmokeTest.scala +scala/collection/immutable/ChampSetSmokeTest.scala +scala/collection/immutable/LazyListGCTest.scala +scala/collection/immutable/LazyListLazinessTest.scala +scala/collection/immutable/ListTest.scala +scala/collection/immutable/SerializationTest.scala +scala/collection/immutable/StreamTest.scala +scala/collection/immutable/StringLikeTest.scala +scala/collection/immutable/VectorTest.scala +scala/collection/mutable/AnyRefMapTest.scala +scala/collection/mutable/ArrayBufferTest.scala +scala/collection/mutable/ListBufferTest.scala +scala/collection/mutable/OpenHashMapTest.scala +scala/collection/mutable/PriorityQueueTest.scala +scala/collection/mutable/SerializationTest.scala +scala/concurrent/FutureTest.scala +scala/concurrent/duration/SerializationTest.scala +scala/concurrent/impl/DefaultPromiseTest.scala +scala/io/SourceTest.scala +scala/jdk/AccumulatorTest.scala +scala/jdk/DurationConvertersTest.scala +scala/jdk/FunctionConvertersTest.scala +scala/jdk/OptionConvertersTest.scala +scala/jdk/StepperConversionTest.scala +scala/jdk/StepperTest.scala +scala/jdk/StreamConvertersTest.scala +scala/jdk/StreamConvertersTypingTest.scala +scala/math/OrderingTest.scala +scala/runtime/ScalaRunTimeTest.scala +scala/sys/env.scala +scala/sys/process/ParserTest.scala +scala/sys/process/PipedProcessTest.scala +scala/sys/process/ProcessBuilderTest.scala +scala/sys/process/ProcessTest.scala +scala/tools/testkit/AssertUtilTest.scala +scala/util/PropertiesTest.scala +scala/util/SpecVersionTest.scala +scala/util/SystemPropertiesTest.scala + +## Tests fail + +# Reflection +scala/reflect/ClassTagTest.scala + +# Require strict-floats +scala/math/BigDecimalTest.scala + +# Tests passed but are too slow (timeouts) +scala/collection/immutable/ListSetTest.scala +scala/util/SortingTest.scala + +# Relies on undefined behavior +scala/collection/MapTest.scala +scala/collection/StringOpsTest.scala +scala/collection/StringParsersTest.scala +scala/collection/convert/CollectionConvertersTest.scala +scala/collection/convert/MapWrapperTest.scala +scala/math/BigIntTest.scala diff --git a/test-adapter/src/main/scala/org/scalajs/testing/adapter/TestAdapter.scala b/test-adapter/src/main/scala/org/scalajs/testing/adapter/TestAdapter.scala index ae7b7d44dd..0172b89964 100644 --- a/test-adapter/src/main/scala/org/scalajs/testing/adapter/TestAdapter.scala +++ b/test-adapter/src/main/scala/org/scalajs/testing/adapter/TestAdapter.scala @@ -44,7 +44,7 @@ final class TestAdapter(jsEnv: JSEnv, input: Seq[Input], config: TestAdapter.Con /** A custom execution context that delegates to the global one for execution, * but handles failures internally. */ - private implicit val executionContext = + private implicit val executionContext: ExecutionContext = ExecutionContext.fromExecutor(ExecutionContext.global, reportFailure) /** Creates an `sbt.testing.Framework` for each framework that can be found. From ef994c9c5ceb3f34a39781958e1b278b6dc21d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 19 Jun 2023 13:32:58 +0200 Subject: [PATCH 24/27] Fix #4878: Avoid overflow while checking bounds in regionMatches. --- javalib/src/main/scala/java/lang/_String.scala | 4 ++-- .../scalajs/testsuite/javalib/lang/StringTest.scala | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/javalib/src/main/scala/java/lang/_String.scala b/javalib/src/main/scala/java/lang/_String.scala index 1daea18ac3..e016d070d4 100644 --- a/javalib/src/main/scala/java/lang/_String.scala +++ b/javalib/src/main/scala/java/lang/_String.scala @@ -293,8 +293,8 @@ final class _String private () // scalastyle:ignore ooffset: Int, len: Int): scala.Boolean = { if (other == null) { throw new NullPointerException() - } else if (toffset < 0 || ooffset < 0 || toffset + len > this.length() || - ooffset + len > other.length()) { + } else if (toffset < 0 || ooffset < 0 || len > this.length() - toffset || + len > other.length() - ooffset) { false } else if (len <= 0) { true diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringTest.scala index fb88a1620b..49b7786a2e 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringTest.scala @@ -533,6 +533,18 @@ class StringTest { assertFalse(test.regionMatches(100, test, 0, -4)) assertFalse(test.regionMatches(0, test, 100, -4)) + // offset + len > length + assertFalse(test.regionMatches(3, "defg", 0, 4)) // on receiver string + assertFalse(test.regionMatches(3, "abcde", 3, 3)) // on other string + assertFalse(test.regionMatches(Int.MaxValue, "ab", 0, 1)) // #4878 overflow, large toffset + assertFalse(test.regionMatches(0, "ab", Int.MaxValue, 1)) // #4878 overflow, large ooffset + assertFalse(test.regionMatches(1, "ab", 1, Int.MaxValue)) // #4878 overflow, large len + assertFalse(test.regionMatches(true, 3, "defg", 0, 4)) // on receiver string + assertFalse(test.regionMatches(true, 3, "abcde", 3, 3)) // on other string + assertFalse(test.regionMatches(true, Int.MaxValue, "ab", 0, 1)) // #4878 overflow, large toffset + assertFalse(test.regionMatches(true, 0, "ab", Int.MaxValue, 1)) // #4878 overflow, large ooffset + assertFalse(test.regionMatches(true, 1, "ab", 1, Int.MaxValue)) // #4878 overflow, large len + // the strange cases that are true assertTrue(test.regionMatches(0, test, 0, -4)) assertTrue(test.regionMatches(1, "bcdx", 0, -4)) From 26c7390351c05380864f367aa797f004914530a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Tue, 16 May 2023 14:19:04 +0200 Subject: [PATCH 25/27] Refactoring: Focus internal module ID generation in InternalModuleIDGenerator. It is an evolution of `ModuleIDs` into a pair of classes that store more information about what they need to avoid, so that we don't have to repeat it at use site. It also concentrates all the logic about internal module ID generation, which will allow the future fix for case-insensitive filesystems to be more localized. --- .../FewestModulesAnalyzer.scala | 6 +- .../InternalModuleIDGenerator.scala | 96 +++++++++++++++++++ .../frontend/modulesplitter/ModuleIDs.scala | 71 -------------- .../SmallModulesForAnalyzer.scala | 13 +-- .../SmallestModulesAnalyzer.scala | 7 +- .../frontend/modulesplitter/Tagger.scala | 21 ++-- .../InternalModuleIDGeneratorTest.scala | 63 ++++++++++++ 7 files changed, 179 insertions(+), 98 deletions(-) create mode 100644 linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala delete mode 100644 linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/ModuleIDs.scala create mode 100644 linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/FewestModulesAnalyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/FewestModulesAnalyzer.scala index f5b2522705..5b96f5eaf3 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/FewestModulesAnalyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/FewestModulesAnalyzer.scala @@ -32,10 +32,8 @@ private[modulesplitter] final class FewestModulesAnalyzer extends ModuleAnalyzer // Fast path. new SingleModuleAnalysis(info.publicModuleDependencies.head._1) } else { - val prefix = ModuleIDs.freeInternalPrefix( - avoid = info.publicModuleDependencies.keys) - - val moduleMap = new Tagger(info).tagAll(prefix) + val modulesToAvoid = info.publicModuleDependencies.keys + val moduleMap = new Tagger(info).tagAll(modulesToAvoid) new FullAnalysis(moduleMap) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala new file mode 100644 index 0000000000..6afadd7dc5 --- /dev/null +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala @@ -0,0 +1,96 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.frontend.modulesplitter + +import org.scalajs.ir.Names.{ClassName, ObjectClass} +import org.scalajs.linker.standard.ModuleSet.ModuleID + +/** Generators for internal module IDs. */ +private[modulesplitter] object InternalModuleIDGenerator { + + /** Generator based on `ClassName`s. */ + final class ForClassNames(avoid: Iterable[ModuleID]) { + private val avoidSet: Set[ModuleID] = avoid.toSet + + /** Picks a representative from a list of classes. + * + * Guarantees to return the same value independent of the order of [[names]]. + */ + def representativeClass(names: List[ClassName]): ClassName = { + require(names.nonEmpty) + + /* Take the lexicographically smallest name as a stable name of the + * module, with the exception of j.l.Object which identifies the root + * module. + * + * We do this, because it is simple and stable (i.e. does not depend + * on traversal order). + */ + if (names.contains(ObjectClass)) ObjectClass + else names.min + } + + /** Builds an ID for the class with name [[name]]. + * + * The result is guaranteed to be: + * - Different from any public module ID. + * - Different for each ClassName. + * - Deterministic. + */ + def forClassName(name: ClassName): ModuleID = { + /* Build a module ID that doesn't collide with others. + * + * We observe: + * - Class names are unique, so they never collide with each other. + * - Appending a dot ('.') to a class name results in an illegal class name. + * + * So we append dots until we hit a ModuleID not used by a public module. + * + * Note that this is stable, because it does not depend on the order we + * iterate over nodes. + */ + var moduleID = ModuleID(name.nameString) + while (avoidSet.contains(moduleID)) + moduleID = ModuleID(moduleID.id + ".") + moduleID + } + } + + /** Generator based on digests. */ + final class ForDigests private (internalModuleIDPrefix: String) { + def this(avoid: Iterable[ModuleID]) = + this(freeInternalPrefix(avoid)) + + def forDigest(digest: Array[Byte]): ModuleID = { + @inline def hexDigit(digit: Int): Char = + Character.forDigit(digit & 0x0f, 16) + + val id = new java.lang.StringBuilder(internalModuleIDPrefix) + + for (b <- digest) { + id.append(hexDigit(b >> 4)) + id.append(hexDigit(b)) + } + + ModuleID(id.toString()) + } + } + + /** Creates a prefix that is not a prefix of any of the IDs in [[avoid]] */ + private def freeInternalPrefix(avoid: Iterable[ModuleID]): String = { + Iterator + .iterate("internal-")(_ + "-") + .find(p => !avoid.exists(_.id.startsWith(p))) + .get + } +} diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/ModuleIDs.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/ModuleIDs.scala deleted file mode 100644 index 888b2b8d6e..0000000000 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/ModuleIDs.scala +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.linker.frontend.modulesplitter - -import org.scalajs.ir.Names.{ClassName, ObjectClass} -import org.scalajs.linker.standard.ModuleSet.ModuleID - -/** Helpers to create internal ModulesIDs */ -private object ModuleIDs { - - /** Picks a representative from a list of classes. - * - * Guarantees to return the same value independent of the order of [[names]]. - */ - def representativeClass(names: List[ClassName]): ClassName = { - require(names.nonEmpty) - - /* Take the lexicographically smallest name as a stable name of the - * module, with the exception of j.l.Object which identifies the root - * module. - * - * We do this, because it is simple and stable (i.e. does not depend - * on traversal order). - */ - if (names.contains(ObjectClass)) ObjectClass - else names.min - } - - /** Builds an ID for the class with name [[name]]. - * - * The result is guaranteed to be: - * - Different from any ModuleID in [[avoid]]. - * - Different for each ClassName. - * - Deterministic. - */ - def forClassName(avoid: Set[ModuleID], name: ClassName): ModuleID = { - /* Build a module ID that doesn't collide with others. - * - * We observe: - * - Class names are unique, so they never collide with each other. - * - Appending a dot ('.') to a class name results in an illegal class name. - * - * So we append dots until we hit a ModuleID not used by a public module. - * - * Note that this is stable, because it does not depend on the order we - * iterate over nodes. - */ - var moduleID = ModuleID(name.nameString) - while (avoid.contains(moduleID)) - moduleID = ModuleID(moduleID.id + ".") - moduleID - } - - /** Creates a prefix that is not a prefix of any of the IDs in [[avoid]] */ - def freeInternalPrefix(avoid: Iterable[ModuleID]): String = { - Iterator - .iterate("internal-")(_ + "-") - .find(p => !avoid.exists(_.id.startsWith(p))) - .get - } -} diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallModulesForAnalyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallModulesForAnalyzer.scala index dbf5fcdff9..da4cd3a519 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallModulesForAnalyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallModulesForAnalyzer.scala @@ -34,11 +34,9 @@ private final class SmallModulesForAnalyzer( def analyze(info: ModuleAnalyzer.DependencyInfo): ModuleAnalyzer.Analysis = { val (targetClassToRepr, reprToModuleID) = smallRun(info, packages) - val prefix = ModuleIDs.freeInternalPrefix( - info.publicModuleDependencies.keys ++ reprToModuleID.values) - + val modulesToAvoid = info.publicModuleDependencies.keys ++ reprToModuleID.values val largeModuleMap = - new Tagger(info, excludedClasses = targetClassToRepr.keySet).tagAll(prefix) + new Tagger(info, excludedClasses = targetClassToRepr.keySet).tagAll(modulesToAvoid) new SmallModulesForAnalyzer.Analysis(targetClassToRepr, reprToModuleID, largeModuleMap) } @@ -67,6 +65,9 @@ private object SmallModulesForAnalyzer { private final class SmallRun(info: ModuleAnalyzer.DependencyInfo, packages: List[ClassName]) extends StrongConnect(info) { + private val internalModIDGenerator = + new InternalModuleIDGenerator.ForClassNames(info.publicModuleDependencies.keys) + /* We expect this to contain relatively few classes. * * So instead of keeping the underlying graph and relying on [[moduleIndex]], @@ -81,8 +82,8 @@ private object SmallModulesForAnalyzer { val targetNames = classNames.filter(clazz => packages.exists(inPackage(clazz, _))) if (targetNames.nonEmpty) { - val repr = ModuleIDs.representativeClass(targetNames) - val id = ModuleIDs.forClassName(info.publicModuleDependencies.keySet, repr) + val repr = internalModIDGenerator.representativeClass(targetNames) + val id = internalModIDGenerator.forClassName(repr) reprToModuleID(repr) = id for (className <- classNames) targetClassToRepr(className) = repr diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallestModulesAnalyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallestModulesAnalyzer.scala index fc827978ee..d50a45fda9 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallestModulesAnalyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/SmallestModulesAnalyzer.scala @@ -37,14 +37,17 @@ private[modulesplitter] object SmallestModulesAnalyzer { private final class Run(info: ModuleAnalyzer.DependencyInfo) extends StrongConnect(info) with ModuleAnalyzer.Analysis { + private val internalModIDGenerator = + new InternalModuleIDGenerator.ForClassNames(info.publicModuleDependencies.keys) + private[this] val moduleIndexToID = mutable.Map.empty[Int, ModuleID] def moduleForClass(className: ClassName): Option[ModuleID] = moduleIndex(className).map(moduleIndexToID) protected def emitModule(moduleIndex: Int, classNames: List[ClassName]): Unit = { - val repr = ModuleIDs.representativeClass(classNames) - val id = ModuleIDs.forClassName(info.publicModuleDependencies.keySet, repr) + val repr = internalModIDGenerator.representativeClass(classNames) + val id = internalModIDGenerator.forClassName(repr) moduleIndexToID(moduleIndex) = id } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/Tagger.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/Tagger.scala index f09088be26..8695eef4b9 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/Tagger.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/Tagger.scala @@ -24,6 +24,7 @@ import org.scalajs.ir.Names.ClassName import org.scalajs.ir.SHA1 import org.scalajs.linker.standard.ModuleSet.ModuleID +import InternalModuleIDGenerator.ForDigests /** Tagger groups classes into coarse modules. * @@ -163,13 +164,14 @@ private class Tagger(infos: ModuleAnalyzer.DependencyInfo, private[this] val allPaths = mutable.Map.empty[ClassName, Paths] - final def tagAll(internalModuleIDPrefix: String): scala.collection.Map[ClassName, ModuleID] = { + final def tagAll(modulesToAvoid: Iterable[ModuleID]): scala.collection.Map[ClassName, ModuleID] = { + val internalModIDGenerator = new InternalModuleIDGenerator.ForDigests(modulesToAvoid) tagEntryPoints() for { (className, paths) <- allPaths if !excludedClasses.contains(className) } yield { - className -> paths.moduleID(internalModuleIDPrefix) + className -> paths.moduleID(internalModIDGenerator) } } @@ -247,7 +249,7 @@ private object Tagger { hopCountsChanged || stepsChanged } - def moduleID(internalModuleIDPrefix: String): ModuleID = { + def moduleID(internalModIDGenerator: ForDigests): ModuleID = { if (direct.size == 1 && dynamic.isEmpty && maxExcludedHopCount == 0) { /* Class is only used by a single public module. Put it there. * @@ -276,18 +278,7 @@ private object Tagger { for (className <- dynamicEnds) digestBuilder.updateUTF8String(className.encoded) - // Build a hex string of the hash with the right prefix. - @inline def hexDigit(digit: Int): Char = - Character.forDigit(digit & 0x0f, 16) - - val id = new java.lang.StringBuilder(internalModuleIDPrefix) - - for (b <- digestBuilder.finalizeDigest()) { - id.append(hexDigit(b >> 4)) - id.append(hexDigit(b)) - } - - ModuleID(id.toString()) + internalModIDGenerator.forDigest(digestBuilder.finalizeDigest()) } } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala new file mode 100644 index 0000000000..2f310a7050 --- /dev/null +++ b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala @@ -0,0 +1,63 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.frontend.modulesplitter + +import org.junit.Test +import org.junit.Assert._ + +import org.scalajs.ir.Names.ClassName + +import org.scalajs.linker.standard.ModuleSet.ModuleID + +/** Whitebox tests for `InternalModuleIDGenerator`. */ +class InternalModuleIDGeneratorTest { + @Test def testForClassName(): Unit = { + val testPublicModuleIDs = List(ModuleID("test.Public"), ModuleID("test.OtherPublic")) + val generator = new InternalModuleIDGenerator.ForClassNames(testPublicModuleIDs) + + def test(expected: String, classNameString: String): Unit = + assertEquals(expected, generator.forClassName(ClassName(classNameString)).id) + + test("java.lang.String", "java.lang.String") + test("java.lang.StringBuilder", "java.lang.StringBuilder") + + test("test-S.foo--Bar", "test-S.foo--Bar") + + test("test.été", "test.été") + test("test.Été", "test.Été") + + test("test.dz", "test.dz") // U+01F3 Latin Small Letter Dz + test("test.DZ", "test.DZ") // U+01F1 Latin Capital Letter Dz + test("test.Dz", "test.Dz") // U+01F2 Latin Capital Letter D with Small Letter Z + + test("test.Public.", "test.Public") + test("test.OtherPublic.", "test.OtherPublic") + } + + @Test def testForDigest(): Unit = { + val goodModuleID = ModuleID("good") + val otherGoodModuleID = ModuleID("othergood") + val collidingModuleID = ModuleID("internal-mod") + + val digest = Array(0x12.toByte, 0x34.toByte, 0xef.toByte) + + val generator1 = new InternalModuleIDGenerator.ForDigests(Nil) + assertEquals("internal-1234ef", generator1.forDigest(digest).id) + + val generator2 = new InternalModuleIDGenerator.ForDigests(List(goodModuleID, otherGoodModuleID)) + assertEquals("internal-1234ef", generator2.forDigest(digest).id) + + val generator3 = new InternalModuleIDGenerator.ForDigests(List(goodModuleID, collidingModuleID)) + assertEquals("internal--1234ef", generator3.forDigest(digest).id) + } +} From 2c64d54d95362b0c7afdc166910be4b6b662024a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Tue, 16 May 2023 14:44:35 +0200 Subject: [PATCH 26/27] Fix #4855: Avoid case-insensitive clashes for internal module IDs. When an internal module contains a single class, we name it after the class name. This is supposed to give unambiguous module IDs. However, it is not good enough on case-insensitive file systems, as we can have two classes whose names differ only in case. To avoid the issue, we prefix uppercase ASCII characters with a '-' and all non-ASCII characters with a '-' and their code point value. We have a similar issue for digest-based module IDs. Their 'internal-' prefix must also be protected for collision in a case-insensitive way. For that, we can directly compare with prefixes of the IDs `equalsIgnoreCase`. --- appveyor.yml | 2 + .../InternalModuleIDGenerator.scala | 128 ++++++++++++++++-- .../linker/SmallModulesForSplittingTest.scala | 4 +- .../linker/SmallestModulesSplittingTest.scala | 6 +- .../InternalModuleIDGeneratorTest.scala | 30 ++-- project/Build.scala | 16 +++ .../compiler/ClassdiffersOnlyinCase.scala | 19 +++ .../compiler/ClassDiffersOnlyInCaseTest.scala | 27 ++++ .../compiler/ClassDiffersOnlyIncase.scala | 19 +++ 9 files changed, 225 insertions(+), 26 deletions(-) create mode 100644 test-suite/shared/src/main/scala/org/scalajs/testsuite/compiler/ClassdiffersOnlyinCase.scala create mode 100644 test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyInCaseTest.scala create mode 100644 test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyIncase.scala diff --git a/appveyor.yml b/appveyor.yml index efdcf8ee5b..4f0c93e14c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,6 +15,8 @@ test_script: # Very far from testing everything, but at least it is a good sanity check # For slow things (partest and scripted), we execute only one test - cmd: sbt ";clean;testSuite2_12/test;linker2_12/test;partestSuite2_12/testOnly -- --fastOpt run/option-fold.scala" + # Module splitting has some logic for case-insensitive filesystems, which we must test on Windows + - cmd: sbt ";setSmallESModulesForAppVeyorCI;testSuite2_12/test" cache: - C:\sbt - C:\Users\appveyor\.ivy2\cache diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala index 6afadd7dc5..82adb9b823 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala @@ -12,15 +12,37 @@ package org.scalajs.linker.frontend.modulesplitter +import scala.collection.immutable.SortedSet + import org.scalajs.ir.Names.{ClassName, ObjectClass} import org.scalajs.linker.standard.ModuleSet.ModuleID -/** Generators for internal module IDs. */ +/** Generators for internal module IDs. + * + * In order to support case-insensitive file systems, the methods in this + * class all consider equality of module names as being case-insensitive. + * To be more precise, we use the *simple default casing* rules of Unicode + * for the default locale, without normalization. + * + * The reference file in Unicode on case-insensitivy is about case folding: + * https://unicode.org/Public/UNIDATA/CaseFolding.txt + * + * - The "simple" rules do not include case conversions that make a string + * longer. For example, we do not handle the fact that "ß" is equal to "SS" + * as well as "ss". + * - We do not use the Turkish-specific rules. Instead, we consider that all + * of 'i ı I İ' are equal. + * + * We only have to ensure that we never generate names that may collide. We + * do not have to *optimally* do so. Therefore, it is fine to always consider + * all the 'i's to be the same, for example. + */ private[modulesplitter] object InternalModuleIDGenerator { /** Generator based on `ClassName`s. */ final class ForClassNames(avoid: Iterable[ModuleID]) { - private val avoidSet: Set[ModuleID] = avoid.toSet + private val avoidSet: Set[String] = + SortedSet(avoid.map(_.id).toSeq: _*)(CaseInsensitiveStringOrdering) /** Picks a representative from a list of classes. * @@ -58,11 +80,51 @@ private[modulesplitter] object InternalModuleIDGenerator { * * Note that this is stable, because it does not depend on the order we * iterate over nodes. + * + * To deal with case-insensitive issues, basically we prefix every + * uppercase character with a '-', and we prefix every '-' with a '-' to + * avoid clashes. However, that is not good enough, since several + * uppercase (and titlecase) code points can case-fold to the same + * lowercase letter. Therefore, the complete scheme is: + * + * - ASCII uppercase letters are prefixed with '-'. + * - '-' is prefixed with '-'. + * - Non-ASCII characters are all prefixed by '-u' followed by the 6 + * hexdigits of their codepoint. + * + * The last rule is far from being optimal, but it is safe. Encountering + * non-ASCII characters in class names should be rare anyway. */ - var moduleID = ModuleID(name.nameString) - while (avoidSet.contains(moduleID)) - moduleID = ModuleID(moduleID.id + ".") - moduleID + + val builder = new java.lang.StringBuilder + + // First, encode uppercase characters to avoid accidental case-insensitive clashes + val originalNameString = name.nameString + val originalNameStringLen = originalNameString.length() + var i = 0 + while (i != originalNameStringLen) { + val cp = originalNameString.codePointAt(i) + if (cp < 0x80) { + // ASCII + if (cp == '-' || (cp >= 'A' && cp <= 'Z')) + builder.append('-') + builder.append(cp.toChar) + i += 1 + } else { + // Non-ASCII + new java.util.Formatter(builder).format("-u%06x", Integer.valueOf(cp)) + builder.appendCodePoint(cp) + i += Character.charCount(cp) + } + } + + // Second, avoid colliding with the public module IDs in `avoidSet` + var candidateID = builder.toString() + while (avoidSet.contains(candidateID)) { + builder.append('.') + candidateID = builder.toString() + } + ModuleID(candidateID) } } @@ -88,9 +150,55 @@ private[modulesplitter] object InternalModuleIDGenerator { /** Creates a prefix that is not a prefix of any of the IDs in [[avoid]] */ private def freeInternalPrefix(avoid: Iterable[ModuleID]): String = { - Iterator - .iterate("internal-")(_ + "-") - .find(p => !avoid.exists(_.id.startsWith(p))) - .get + /* Here we can use `equalsIgnoreCase`, even though it has a poor notion of + * case folding (which is even Char-based, not code point-based). That is + * because we always compare against a string of the form 'internal---' for + * an arbitrary number of '-'. + * + * - Only '-' is equal to '-' + * - Only 'i ı I İ' are equal to 'i' + * - Only ASCII letters are equal to the other letters of "internal" + * + * All these cases are handled by `equalsIgnoreCase`. + */ + + val BasePrefix = "internal" + val BasePrefixLen = BasePrefix.length() + + // Does `id` start with "internal-", ignoring case + def startsWith_internalDash(id: String): Boolean = { + id.length() > BasePrefixLen && + id.charAt(BasePrefixLen) == '-' && // fast exit (avoid `substring`+`equalsIgnoreCase`) + id.substring(0, BasePrefixLen).equalsIgnoreCase(BasePrefix) + } + + // The first index of `id` after "internal" that is not a '-' (possibly `id.length()`). + def findFirstNonDashIndex(id: String): Int = { + val indexOrNegative = id.indexWhere(_ != '-', from = BasePrefixLen) + if (indexOrNegative < 0) + id.length() + else + indexOrNegative + } + + def longestPrefixOfIDLike_internalDashes(id: ModuleID): Int = { + if (startsWith_internalDash(id.id)) + findFirstNonDashIndex(id.id) + else + 0 + } + + val longestPrefixLike_internalDashes = + if (avoid.isEmpty) 0 + else avoid.iterator.map(longestPrefixOfIDLike_internalDashes(_)).max + + // Our prefix must be longer than that + val freePrefixLen = longestPrefixLike_internalDashes + 1 + val requiredDashCount = Math.max(freePrefixLen - BasePrefixLen, 1) + BasePrefix + ("-" * requiredDashCount) + } + + private object CaseInsensitiveStringOrdering extends Ordering[String] { + def compare(x: String, y: String): Int = x.compareToIgnoreCase(y) } } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala index 44390e4039..1fefebf792 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala @@ -83,8 +83,8 @@ class SmallModulesForSplittingTest { module.classDefs.map(_.name.name) } - assertEquals(List[ClassName]("foo.A"), moduleClasses("foo.A")) - assertEquals(List[ClassName]("foo.C"), moduleClasses("foo.C")) + assertEquals(List[ClassName]("foo.A"), moduleClasses("foo.-A")) + assertEquals(List[ClassName]("foo.C"), moduleClasses("foo.-C")) assertEquals(List(MainTestClassName), moduleClasses("main")) /* Expect two additional modules, one for each: diff --git a/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala index f385497320..94b8340753 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala @@ -61,9 +61,9 @@ class SmallestModulesSplittingTest { ) val expectedFiles = Set( - "java.lang.Object.js", - "Test.js", - "lib.Greeter.js", + "java.lang.-Object.js", + "-Test.js", + "lib.-Greeter.js", "main.js" ) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala index 2f310a7050..1b6dceb216 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala @@ -22,32 +22,34 @@ import org.scalajs.linker.standard.ModuleSet.ModuleID /** Whitebox tests for `InternalModuleIDGenerator`. */ class InternalModuleIDGeneratorTest { @Test def testForClassName(): Unit = { - val testPublicModuleIDs = List(ModuleID("test.Public"), ModuleID("test.OtherPublic")) + val testPublicModuleIDs = List(ModuleID("test.-Public"), ModuleID("test.-Other-Public")) val generator = new InternalModuleIDGenerator.ForClassNames(testPublicModuleIDs) def test(expected: String, classNameString: String): Unit = assertEquals(expected, generator.forClassName(ClassName(classNameString)).id) - test("java.lang.String", "java.lang.String") - test("java.lang.StringBuilder", "java.lang.StringBuilder") + test("java.lang.-String", "java.lang.String") + test("java.lang.-String-Builder", "java.lang.StringBuilder") - test("test-S.foo--Bar", "test-S.foo--Bar") + test("test---S.foo-----Bar", "test-S.foo--Bar") - test("test.été", "test.été") - test("test.Été", "test.Été") + test("test.-u0000e9ét-u0000e9é", "test.été") + test("test.-u0000c9Ét-u0000e9é", "test.Été") - test("test.dz", "test.dz") // U+01F3 Latin Small Letter Dz - test("test.DZ", "test.DZ") // U+01F1 Latin Capital Letter Dz - test("test.Dz", "test.Dz") // U+01F2 Latin Capital Letter D with Small Letter Z + test("test.-u0001f3dz", "test.dz") // U+01F3 Latin Small Letter Dz + test("test.-u0001f1DZ", "test.DZ") // U+01F1 Latin Capital Letter Dz + test("test.-u0001f2Dz", "test.Dz") // U+01F2 Latin Capital Letter D with Small Letter Z - test("test.Public.", "test.Public") - test("test.OtherPublic.", "test.OtherPublic") + test("test.-Public.", "test.Public") + test("test.-Other-Public.", "test.OtherPublic") } @Test def testForDigest(): Unit = { val goodModuleID = ModuleID("good") val otherGoodModuleID = ModuleID("othergood") val collidingModuleID = ModuleID("internal-mod") + val collidingCaseInsensitiveModuleID = ModuleID("InTernal--mod") + val collidingCaseInsensitiveModuleID2 = ModuleID("İnTernal-mod") // U+0130 Latin Capital Letter I with Dot Above val digest = Array(0x12.toByte, 0x34.toByte, 0xef.toByte) @@ -59,5 +61,11 @@ class InternalModuleIDGeneratorTest { val generator3 = new InternalModuleIDGenerator.ForDigests(List(goodModuleID, collidingModuleID)) assertEquals("internal--1234ef", generator3.forDigest(digest).id) + + val generator4 = new InternalModuleIDGenerator.ForDigests(List(collidingCaseInsensitiveModuleID, goodModuleID)) + assertEquals("internal---1234ef", generator4.forDigest(digest).id) + + val generator5 = new InternalModuleIDGenerator.ForDigests(List(collidingCaseInsensitiveModuleID2, goodModuleID)) + assertEquals("internal--1234ef", generator5.forDigest(digest).id) } } diff --git a/project/Build.scala b/project/Build.scala index 4dbeba6445..c5a1dc24a5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -118,6 +118,15 @@ object MyScalaJSPlugin extends AutoPlugin { fullClasspath in scalaJSLinkerImpl := { (fullClasspath in (Build.linker.v2_12, Runtime)).value }, + + /* The AppVeyor CI build definition is very sensitive to weird characthers + * in its command lines, so we cannot directly spell out the correct + * incantation. Instead, we define this alias. + */ + addCommandAlias( + "setSmallESModulesForAppVeyorCI", + "set testSuite.v2_12 / scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule).withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List(\"org.scalajs.testsuite\"))))" + ), ) override def projectSettings: Seq[Setting[_]] = Def.settings( @@ -1901,6 +1910,13 @@ object Build { testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-s"), + unmanagedSourceDirectories in Compile ++= { + val mainDir = (sourceDirectory in Compile).value + val sharedMainDir = mainDir.getParentFile.getParentFile.getParentFile / "shared/src/main" + + List(sharedMainDir / "scala") + }, + unmanagedSourceDirectories in Test ++= { val testDir = (sourceDirectory in Test).value val sharedTestDir = diff --git a/test-suite/shared/src/main/scala/org/scalajs/testsuite/compiler/ClassdiffersOnlyinCase.scala b/test-suite/shared/src/main/scala/org/scalajs/testsuite/compiler/ClassdiffersOnlyinCase.scala new file mode 100644 index 0000000000..55fdf95131 --- /dev/null +++ b/test-suite/shared/src/main/scala/org/scalajs/testsuite/compiler/ClassdiffersOnlyinCase.scala @@ -0,0 +1,19 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.compiler + +@noinline +class ClassdiffersOnlyinCase { + @noinline + def provenance: String = "main" +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyInCaseTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyInCaseTest.scala new file mode 100644 index 0000000000..e8403038af --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyInCaseTest.scala @@ -0,0 +1,27 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.compiler + +import org.junit.Test +import org.junit.Assert._ + +class ClassDiffersOnlyInCaseTest { + @Test + def testClassesThatDifferOnlyInCase_Issue4855(): Unit = { + val fromMain = new ClassdiffersOnlyinCase() + assertEquals("main", fromMain.provenance) + + val fromTest = new ClassDiffersOnlyIncase() + assertEquals("test", fromTest.provenance) + } +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyIncase.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyIncase.scala new file mode 100644 index 0000000000..3741b7660c --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/ClassDiffersOnlyIncase.scala @@ -0,0 +1,19 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.compiler + +@noinline +class ClassDiffersOnlyIncase { + @noinline + def provenance: String = "test" +} From 037fa855a636a521721f6b126f915744136cd47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 21 Jun 2023 16:07:01 +0200 Subject: [PATCH 27/27] Version 1.13.2. --- ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala index 438d171d6f..3abc5f11c9 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala @@ -17,7 +17,7 @@ import java.util.concurrent.ConcurrentHashMap import scala.util.matching.Regex object ScalaJSVersions extends VersionChecks( - current = "1.13.2-SNAPSHOT", + current = "1.13.2", binaryEmitted = "1.13" )