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 46050e65b1..5d4ca8efb0 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 @@ -165,7 +165,6 @@ object Analysis { def from: From } - final case class MissingJavaLangObjectClass(from: From) extends Error final case class CycleInInheritanceChain(encodedClassNames: List[ClassName], from: From) extends Error final case class MissingClass(info: ClassInfo, from: From) extends Error @@ -216,8 +215,6 @@ object Analysis { def logError(error: Error, logger: Logger, level: Level): Unit = { val headMsg = error match { - case MissingJavaLangObjectClass(_) => - "Fatal error: java.lang.Object is missing" case CycleInInheritanceChain(encodedClassNames, _) => ("Fatal error: cycle in inheritance chain involving " + encodedClassNames.map(_.nameString).mkString(", ")) 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 207e6300df..1bcc71044b 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 @@ -99,19 +99,11 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, /* Load the java.lang.Object class, and validate it * If it is missing or invalid, we're in deep trouble, and cannot continue. */ - infoLoader.loadInfo(ObjectClass)(workQueue.ec) match { - case None => - _errors += MissingJavaLangObjectClass(fromAnalyzer) - - case Some(future) => - workQueue.enqueue(future) { data => - objectClassInfo = new ClassInfo(data, - unvalidatedSuperClass = None, - unvalidatedInterfaces = Nil, nonExistent = false) - - objectClassInfo.link() - onSuccess() - } + lookupClass(ObjectClass) { clazz => + if (!clazz.nonExistent) { + objectClassInfo = clazz + onSuccess() + } } } @@ -352,7 +344,13 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, _classInfos.get(className) match { case None => val loading = new LoadingClass(className) + + _classInfos(className) = loading + + // Request linking before scheduling the loading to avoid the task queue + // dropping to zero intermittently. loading.requestLink(knownDescendants)(onSuccess) + loading.startLoad() case Some(loading: LoadingClass) => loading.requestLink(knownDescendants)(onSuccess) @@ -377,17 +375,6 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, private val promise = Promise[LoadingResult]() private var knownDescendants = Set[LoadingClass](this) - _classInfos(className) = this - - infoLoader.loadInfo(className)(workQueue.ec) match { - case Some(future) => - workQueue.enqueue(future)(link(_, nonExistent = false)) - - case None => - val data = createMissingClassInfo(className) - link(data, nonExistent = true) - } - def requestLink(knownDescendants: Set[LoadingClass])(onSuccess: LoadingResult => Unit): Unit = { if (knownDescendants.contains(this)) { onSuccess(CycleInfo(Nil, this)) @@ -397,6 +384,17 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } } + def startLoad(): Unit = { + infoLoader.loadInfo(className)(workQueue.ec) match { + case Some(future) => + workQueue.enqueue(future)(link(_, nonExistent = false)) + + case None => + val data = createMissingClassInfo(className) + link(data, nonExistent = true) + } + } + private def link(data: Infos.ClassInfo, nonExistent: Boolean): Unit = { lookupAncestors(data.superClass.toList ++ data.interfaces) { classes => val (superClass, interfaces) = @@ -461,15 +459,11 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, val isNativeJSClass = kind == ClassKind.NativeJSClass || kind == ClassKind.NativeJSModuleClass - // Note: j.l.Object is special and is validated upfront - val superClass: Option[ClassInfo] = - if (className == ObjectClass) unvalidatedSuperClass - else validateSuperClass(unvalidatedSuperClass) + validateSuperClass(unvalidatedSuperClass) val interfaces: List[ClassInfo] = - if (className == ObjectClass) unvalidatedInterfaces - else validateInterfaces(unvalidatedInterfaces) + validateInterfaces(unvalidatedInterfaces) /** Ancestors of this class or interface. * @@ -497,6 +491,11 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, def from = FromClass(this) kind match { + case _ if className == ObjectClass => + assert(superClass.isEmpty) + + None + case ClassKind.Class | ClassKind.ModuleClass | ClassKind.HijackedClass => val superCl = superClass.get // checked by ClassDef checker. if (superCl.kind != ClassKind.Class) { @@ -1479,8 +1478,12 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } private def createMissingClassInfo(className: ClassName): Infos.ClassInfo = { + val superClass = + if (className == ObjectClass) None + else Some(ObjectClass) + new Infos.ClassInfoBuilder(className, ClassKind.Class, - superClass = Some(ObjectClass), interfaces = Nil, jsNativeLoadSpec = None) + superClass = superClass, interfaces = Nil, jsNativeLoadSpec = None) .addMethod(makeSyntheticMethodInfo(NoArgConstructorName, MemberNamespace.Constructor)) .result() } 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 e28017127c..06b8747849 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala @@ -51,7 +51,23 @@ class AnalyzerTest { @Test def missingJavaLangObject(): AsyncResult = await { val analysis = computeAnalysis(Nil, stdlib = TestIRRepo.empty) - assertExactErrors(analysis, MissingJavaLangObjectClass(fromAnalyzer)) + assertContainsError("MissingClass(jlObject)", analysis) { + case MissingClass(ClsInfo(name), fromAnalyzer) => + name == ObjectClass.nameString + } + } + + @Test + def missingJavaLangObjectButOthers(): AsyncResult = await { + val classDefs = Seq(classDef("A", superClass = Some(ObjectClass))) + + val analysis = computeAnalysis(classDefs, + reqsFactory.classData("A"), stdlib = TestIRRepo.empty) + + assertContainsError("MissingClass(jlObject)", analysis) { + case MissingClass(ClsInfo(name), fromAnalyzer) => + name == ObjectClass.nameString + } } @Test