diff --git a/Jenkinsfile b/Jenkinsfile index 7f87dfdae4..03e477a676 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -452,12 +452,11 @@ 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.11"] def otherScalaVersions = [ "2.12.2", "2.12.3", - "2.12.4", "2.12.5", "2.12.6", "2.12.7", @@ -470,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", @@ -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/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/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/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala index 42c0038528..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.1", + current = "1.13.2", binaryEmitted = "1.13" ) 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/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/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/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/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") 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 ac64acd3e1..207e6300df 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} @@ -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.interface.{ESVersion, ModuleKind, ModuleInitializer} +import org.scalajs.linker.frontend.IRLoader +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._ + import Analysis._ import Infos.{NamespacedMethodName, ReachabilityInfo, ReachabilityInfoInClass} -private final class Analyzer(config: CommonPhaseConfig, - moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, - allowAddingSyntheticMethods: Boolean, - checkAbstractReachability: Boolean, - inputProvider: Analyzer.InputProvider, - ec: ExecutionContext) - extends Analysis { +final class Analyzer(config: CommonPhaseConfig, initial: Boolean, + checkIR: Boolean, failOnError: 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, logger))(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. */ - inputProvider.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 @@ -128,18 +143,19 @@ 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. reachInitializers(moduleInitializers) } - private def postLoad(): Unit = { + private def postLoad(moduleInitializers: Seq[ModuleInitializer], + logger: Logger): 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,48 +169,67 @@ 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) + + if (failOnError && _errors.nonEmpty) + reportErrors(logger) + + new Analysis { + val classInfos = infos + val topLevelExportInfos = _topLevelExportInfos + val errors = _errors + } + } + + 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 = { + 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) @@ -202,21 +237,21 @@ private final class Analyzer(config: CommonPhaseConfig, 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) @@ -227,17 +262,14 @@ private final class Analyzer(config: CommonPhaseConfig, 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 } @@ -301,15 +333,12 @@ private final class Analyzer(config: CommonPhaseConfig, } } - 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) @@ -350,7 +379,7 @@ private final class Analyzer(config: CommonPhaseConfig, _classInfos(className) = this - inputProvider.loadInfo(className)(ec) match { + infoLoader.loadInfo(className)(workQueue.ec) match { case Some(future) => workQueue.enqueue(future)(link(_, nonExistent = false)) @@ -553,7 +582,7 @@ private final class Analyzer(config: CommonPhaseConfig, 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 @@ -617,19 +646,19 @@ private final class Analyzer(config: CommonPhaseConfig, } 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 @@ -728,6 +757,7 @@ private final class Analyzer(config: CommonPhaseConfig, val syntheticInfo = makeSyntheticMethodInfo( methodName = methodName, + namespace = MemberNamespace.Public, methodsCalledStatically = List( targetOwner.className -> NamespacedMethodName(MemberNamespace.Public, methodName))) val m = new MethodInfo(this, syntheticInfo) @@ -858,7 +888,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))) @@ -922,6 +952,7 @@ private final class Analyzer(config: CommonPhaseConfig, val syntheticInfo = makeSyntheticMethodInfo( methodName = proxyName, + namespace = MemberNamespace.Public, methodsCalled = List(this.className -> targetName)) val m = new MethodInfo(this, syntheticInfo) m.syntheticKind = MethodSyntheticKind.ReflectiveProxy(targetName) @@ -1450,13 +1481,13 @@ private final class Analyzer(config: CommonPhaseConfig, 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 @@ -1476,25 +1507,7 @@ object Analyzer { private val getSuperclassMethodName = MethodName("getSuperclass", Nil, ClassRef(ClassClass)) - def computeReachability(config: CommonPhaseConfig, - moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, - allowAddingSyntheticMethods: Boolean, - checkAbstractReachability: Boolean, - inputProvider: InputProvider)(implicit ec: ExecutionContext): Future[Analysis] = { - val analyzer = new Analyzer(config, moduleInitializers, symbolRequirements, - allowAddingSyntheticMethods, checkAbstractReachability, inputProvider, 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 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 new file mode 100644 index 0000000000..a372af291c --- /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 + +private[analyzer] 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()) + } +} + +private[analyzer] 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, + postBaseLinker = false, postOptimizer = false, logger) + if (errorCount != 0) { + throw new LinkingException( + s"There were $errorCount ClassDef checking errors.") + } + + case InfoLoader.InternalIRCheck => + val errorCount = ClassDefChecker.check(tree, + postBaseLinker = true, postOptimizer = 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/backend/emitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala index a12406a32d..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 @@ -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 => @@ -170,59 +169,51 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - /** Generates the JS constructor for a class. */ - def genConstructor(tree: LinkedClass, useESClass: Boolean, - initToInline: Option[MethodDef])( + /** 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): 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(superClass.isDefined || className == ObjectClass, + s"Class $className is missing a parent class") - if (useESClass) - genES6Constructor(tree, initToInline) - else - genES5Constructor(tree, initToInline) - } + val jsConstructorFunWithGlobals = + genJSConstructorFun(className, superClass, initToInline, useESClass) - /** Generates the JS constructor for a class, ES5 style. */ - private def genES5Constructor(tree: LinkedClass, - initToInline: Option[MethodDef])( - implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { - import TreeDSL._ - implicit val pos = tree.pos - - val className = tree.name.name + 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 (!tree.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) => - untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(ctorVar, _)) + case Some(_) if shouldExtendJSError(className, superClass) => + untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(className, ctorVar, _)) case Some(parentIdent) => WithGlobals(ctorVar.prototype := js.New(globalVar("h", parentIdent.name), Nil)) } for { - ctorFun <- genJSConstructorFun(tree, initToInline, forESClass = false) + ctorFun <- jsConstructorFunWithGlobals realCtorDef <- globalFunctionDef("c", className, ctorFun.args, ctorFun.restParam, ctorFun.body) inheritableCtorDef <- @@ -242,66 +233,49 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { globalVar("h", className).prototype := ctorVar.prototype ) } - } else { - for { - ctorFun <- genConstructorFunForJSClass(tree) - superCtor <- genJSSuperCtor(tree) - } 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(tree: LinkedClass, - initToInline: Option[MethodDef])( + /** 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): WithGlobals[js.Tree] = { - implicit val pos = tree.pos + globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = { - if (tree.kind.isJSClass) { - for (fun <- genConstructorFunForJSClass(tree)) 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(tree, 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 + ) } } } - 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 +305,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 +335,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 +347,18 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } } - private def genConstructorFunForJSClass(tree: LinkedClass)( - implicit moduleContext: ModuleContext, - globalKnowledge: GlobalKnowledge): WithGlobals[js.Function] = { - implicit val pos = tree.pos - - require(tree.kind.isJSClass) + private def chainPrototypeWithLocalCtor(className: ClassName, ctorVar: js.Tree, + superCtor: js.Tree)(implicit pos: Position): js.Tree = { + import TreeDSL._ - val JSConstructorDef(_, params, restParam, body) = tree.jsConstructorDef.getOrElse { - throw new IllegalArgumentException( - s"${tree.className} does not have an exported constructor") - } + val dummyCtor = fileLevelVar("hh", genName(className)) - desugarToFunction(tree.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. */ @@ -407,16 +378,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 +402,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 +420,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 +428,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 +459,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 +480,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 +544,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 +554,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 +641,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 +695,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 +726,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 +749,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } val needIsFunction = !isHijackedClass && { - !tree.kind.isClass || + !kind.isClass || globalKnowledge.isAncestorOfHijackedClass(className) } @@ -831,14 +787,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 +851,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 +868,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 +879,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)) { @@ -953,9 +903,17 @@ 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, tree.jsNativeLoadSpec, + jsCtor <- genJSClassConstructor(className, jsNativeLoadSpec, keepOnlyDangerousVarNames = true) } yield { genArrowFunction(List(js.ParamDef(js.Ident("x"))), None, js.Return { @@ -974,7 +932,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 +949,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 +976,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 +1016,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 +1169,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..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 @@ -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) @@ -460,27 +460,45 @@ 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)) } - ctorCache.getOrElseUpdate(ctorVersion, - classEmitter.genConstructor(linkedClass, useESClass, linkedInlineableInit)(moduleContext, ctorCache)) } /* 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 +548,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 +609,30 @@ 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) + if (linkedClass.kind.hasModuleAccessor && linkedClass.hasInstances) { addToMain(classTreeCache.moduleAccessor.getOrElseUpdate( - classEmitter.genModuleAccessor(linkedClass)(moduleContext, classCache))) + classEmitter.genModuleAccessor(className, kind)(moduleContext, classCache, linkedClass.pos))) + } // Static fields @@ -593,14 +640,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 +900,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 +909,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 +917,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 +929,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 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 c18c0ac2e9..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 @@ -13,7 +13,6 @@ package org.scalajs.linker.frontend import scala.concurrent._ -import scala.util.Try import org.scalajs.logging._ @@ -36,7 +35,9 @@ import Analysis._ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { import BaseLinker._ - private val irLoader = new IRLoader(checkIR) + private val irLoader = new FileIRLoader + private val analyzer = + new Analyzer(config, initial = true, checkIR = checkIR, failOnError = true, irLoader) private val methodSynthesizer = new MethodSynthesizer(irLoader) def link(irInput: Seq[IRFile], @@ -45,9 +46,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") { - analyze(moduleInitializers, symbolRequirements, logger) + analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } linkResult <- logger.timeFuture("Linker: Assemble LinkedClasses") { assemble(moduleInitializers, analysis) @@ -66,52 +67,19 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { linkResult } - result.andThen { case _ => irLoader.cleanAfterRun() } - } - - 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(config, moduleInitializers, - symbolRequirements, allowAddingSyntheticMethods = true, - checkAbstractReachability = true, irLoader) - } yield { - if (analysis.errors.nonEmpty) { - reportErrors(analysis.errors) - } - - analysis + result.andThen { case _ => + irLoader.cleanAfterRun() } } 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) @@ -179,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/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..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 @@ -14,38 +14,34 @@ 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 analyzer = + new Analyzer(config, initial = false, checkIR = checkIR, failOnError = true, irLoader) 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) val analysis = logger.timeFuture("Refiner: Compute reachability") { - analyze(moduleInitializers, symbolRequirements, logger) + analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } for { @@ -66,47 +62,19 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { linkedTopLevelExports.flatten.toList, moduleInitializers) } - inputProvider.cleanAfterRun() + irLoader.cleanAfterRun() result } } - - private def analyze(moduleInitializers: Seq[ModuleInitializer], - symbolRequirements: SymbolRequirement, logger: Logger)( - implicit ec: ExecutionContext): Future[Analysis] = { - for { - analysis <- Analyzer.computeReachability(config, moduleInitializers, - symbolRequirements, allowAddingSyntheticMethods = false, - checkAbstractReachability = false, inputProvider) - } 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 { - 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 +83,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()) - } - } - - 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 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 classExists(className: ClassName): Boolean = + classesByName.contains(className) - def getInfo(methodDef: MethodDef): Infos.MethodInfo = { - val cache = caches(methodDef.flags.namespace.ordinal) - .getOrElseUpdate(methodDef.methodName, new MethodDefInfoCache) - cache.getInfo(methodDef) - } + def irFileVersion(className: ClassName): Version = + Version.Unversioned - 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 loadClassDef(className: ClassName)( + implicit ec: ExecutionContext): Future[ClassDef] = { + Future.successful(classesByName(className)) } 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/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..82adb9b823 --- /dev/null +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGenerator.scala @@ -0,0 +1,204 @@ +/* + * 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 scala.collection.immutable.SortedSet + +import org.scalajs.ir.Names.{ClassName, ObjectClass} +import org.scalajs.linker.standard.ModuleSet.ModuleID + +/** 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[String] = + SortedSet(avoid.map(_.id).toSeq: _*)(CaseInsensitiveStringOrdering) + + /** 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. + * + * 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. + */ + + 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) + } + } + + /** 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 = { + /* 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/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/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/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/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/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( 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..1b6dceb216 --- /dev/null +++ b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/InternalModuleIDGeneratorTest.scala @@ -0,0 +1,71 @@ +/* + * 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.-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.-String-Builder", "java.lang.StringBuilder") + + test("test---S.foo-----Bar", "test-S.foo--Bar") + + test("test.-u0000e9ét-u0000e9é", "test.été") + test("test.-u0000c9Ét-u0000e9é", "test.Été") + + 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.-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) + + 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) + + 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/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..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 @@ -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,15 @@ object LinkingUtils { val classDefIRFiles = classDefs.map(MemClassDefIRFile(_)) val injectedIRFiles = StandardLinkerBackend(config).injectedIRFiles - val loader = new IRLoader(checkIR = true) + val irLoader = new FileIRLoader + val analyzer = new Analyzer(CommonPhaseConfig.fromStandardConfig(config), + initial = true, checkIR = true, failOnError = false, irLoader) val logger = new ScalaConsoleLogger(Level.Error) for { baseFiles <- stdlib - irLoader <- loader.update(classDefIRFiles ++ baseFiles ++ injectedIRFiles, logger) - analysis <- Analyzer.computeReachability( - CommonPhaseConfig.fromStandardConfig(config), moduleInitializers, - symbolRequirements, allowAddingSyntheticMethods = true, - checkAbstractReachability = true, irLoader) + _ <- irLoader.update(classDefIRFiles ++ baseFiles ++ injectedIRFiles) + analysis <- analyzer.computeReachability(moduleInitializers, symbolRequirements, logger) } yield { analysis } 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) } 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 _) } 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/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/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( diff --git a/project/Build.scala b/project/Build.scala index 28b6f4d382..c5a1dc24a5 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 => @@ -107,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( @@ -224,18 +244,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]( @@ -254,13 +275,12 @@ 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_", "") - val scalaVersionsUsedForPublishing: Set[String] = - Set(Default2_12ScalaVersion, Default2_13ScalaVersion) val newScalaBinaryVersionsInThisRelease: Set[String] = Set() @@ -316,6 +336,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 @@ -501,6 +523,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 := { @@ -762,6 +795,42 @@ object Build { } val thisBuildSettings = Def.settings( + cross212ScalaVersions := Seq( + "2.12.2", + "2.12.3", + "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.12.18", + ), + 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", + "2.13.11", + ), + + default212ScalaVersion := cross212ScalaVersions.value.last, + default213ScalaVersion := cross213ScalaVersions.value.last, + // JDK version we are running with javaVersion in Global := { val fullVersion = System.getProperty("java.version") @@ -1004,7 +1073,7 @@ object Build { MyScalaJSPlugin ).settings( commonSettings, - scalaVersion := DefaultScalaVersion, + defaultScalaVersionOnlySettings, fatalWarningsSettings, name := "Scala.js linker private library", publishArtifact in Compile := false, @@ -1191,15 +1260,14 @@ 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), 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", @@ -1296,7 +1364,7 @@ object Build { MyScalaJSPlugin ).settings( commonSettings, - scalaVersion := DefaultScalaVersion, + defaultScalaVersionOnlySettings, fatalWarningsSettings, name := "scalajs-javalib-internal", publishArtifact in Compile := false, @@ -1786,8 +1854,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, @@ -1795,12 +1866,12 @@ object Build { fullLinkGz = 35000 to 36000, )) - case Default2_13ScalaVersion => + 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 _ => @@ -1839,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/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", ) } 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 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") 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/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/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/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 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/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) } 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. 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 } 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/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/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" +} 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 = 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)) 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 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()) + } +}