Skip to content

Commit 01f2e96

Browse files
committed
Create AnalyzerRun for easier state management
- resetState becomes unncessary - Using the ExecutionContext is less boilerplate - Factor out logging. This partially reverts 9302a39.
1 parent 28564e0 commit 01f2e96

File tree

1 file changed

+73
-91
lines changed
  • linker/shared/src/main/scala/org/scalajs/linker/analyzer

1 file changed

+73
-91
lines changed

linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala

Lines changed: 73 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ import Infos.{NamespacedMethodName, ReachabilityInfo, ReachabilityInfoInClass}
4545
final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
4646
checkIR: Boolean, failOnError: Boolean, irLoader: IRLoader) {
4747

48-
import Analyzer._
49-
50-
private val allowAddingSyntheticMethods = initial
51-
private val checkAbstractReachability = initial
52-
5348
private val infoLoader: InfoLoader = {
5449
new InfoLoader(irLoader,
5550
if (!checkIR) InfoLoader.NoIRCheck
@@ -58,51 +53,87 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
5853
)
5954
}
6055

61-
private val isNoModule = config.coreSpec.moduleKind == ModuleKind.NoModule
56+
def computeReachability(moduleInitializers: Seq[ModuleInitializer],
57+
symbolRequirements: SymbolRequirement, logger: Logger)(implicit ec: ExecutionContext): Future[Analysis] = {
6258

63-
private var objectClassInfo: ClassInfo = _
64-
private[this] var classLoader: ClassLoader = _
59+
infoLoader.update(logger)
6560

66-
private[this] val _errors = new GrowingList[Error]
61+
val run = new AnalyzerRun(config, initial, infoLoader)(
62+
adjustExecutionContextForParallelism(ec, config.parallel))
6763

68-
private var workTracker: WorkTracker = _
64+
run
65+
.computeReachability(moduleInitializers, symbolRequirements)
66+
.map { _ =>
67+
if (failOnError && run.errors.nonEmpty)
68+
reportErrors(run.errors, logger)
6969

70-
private val fromAnalyzer = FromCore("analyzer")
70+
run
71+
}
72+
.andThen { case _ => infoLoader.cleanAfterRun() }
73+
}
7174

72-
private[this] val _topLevelExportInfos: mutable.Map[(ModuleID, String), TopLevelExportInfo] = emptyThreadSafeMap
75+
private def reportErrors(errors: List[Error], logger: Logger): Unit = {
76+
require(errors.nonEmpty)
7377

74-
def computeReachability(moduleInitializers: Seq[ModuleInitializer],
75-
symbolRequirements: SymbolRequirement, logger: Logger)(implicit ec: ExecutionContext): Future[Analysis] = {
78+
val maxDisplayErrors = {
79+
val propName = "org.scalajs.linker.maxlinkingerrors"
80+
Try(System.getProperty(propName, "20").toInt).getOrElse(20).max(1)
81+
}
7682

77-
computeInternal(moduleInitializers, symbolRequirements, logger)(
78-
adjustExecutionContextForParallelism(ec, config.parallel))
83+
errors
84+
.take(maxDisplayErrors)
85+
.foreach(logError(_, logger, Level.Error))
86+
87+
val skipped = errors.size - maxDisplayErrors
88+
if (skipped > 0)
89+
logger.log(Level.Error, s"Not showing $skipped more linking errors")
90+
91+
if (initial) {
92+
throw new LinkingException("There were linking errors")
93+
} else {
94+
throw new AssertionError(
95+
"There were linking errors after the optimizer has run. " +
96+
"This is a bug, please report it. " +
97+
"You can work around the bug by disabling the optimizer. " +
98+
"In the sbt plugin, this can be done with " +
99+
"`scalaJSLinkerConfig ~= { _.withOptimizer(false) }`.")
100+
}
79101
}
102+
}
80103

81-
/** Internal helper to isolate the execution context. */
82-
private def computeInternal(moduleInitializers: Seq[ModuleInitializer],
83-
symbolRequirements: SymbolRequirement, logger: Logger)(implicit ec: ExecutionContext): Future[Analysis] = {
104+
private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
105+
infoLoader: InfoLoader)(implicit ec: ExecutionContext) extends Analysis {
106+
import AnalyzerRun._
84107

85-
resetState()
108+
private val allowAddingSyntheticMethods = initial
109+
private val checkAbstractReachability = initial
86110

87-
infoLoader.update(logger)
111+
private val isNoModule = config.coreSpec.moduleKind == ModuleKind.NoModule
112+
113+
private val workTracker: WorkTracker = new WorkTracker
114+
private[this] val classLoader: ClassLoader = new ClassLoader
88115

89-
workTracker = new WorkTracker
90-
classLoader = new ClassLoader
116+
private var objectClassInfo: ClassInfo = _
117+
private var _classInfos: scala.collection.Map[ClassName, ClassInfo] = _
91118

119+
def classInfos: scala.collection.Map[ClassName, Analysis.ClassInfo] = _classInfos
120+
121+
private[this] val _errors = new GrowingList[Error]
122+
123+
override def errors: List[Error] = _errors.get()
124+
125+
private val fromAnalyzer = FromCore("analyzer")
126+
127+
private[this] val _topLevelExportInfos: mutable.Map[(ModuleID, String), TopLevelExportInfo] = emptyThreadSafeMap
128+
def topLevelExportInfos: scala.collection.Map[(ModuleID, String), Analysis.TopLevelExportInfo] = _topLevelExportInfos
129+
130+
def computeReachability(moduleInitializers: Seq[ModuleInitializer],
131+
symbolRequirements: SymbolRequirement): Future[Unit] = {
92132
loadObjectClass(() => loadEverything(moduleInitializers, symbolRequirements))
93133

94134
workTracker
95135
.allowComplete()
96-
.map(_ => postLoad(moduleInitializers, logger))
97-
.andThen { case _ => infoLoader.cleanAfterRun() }
98-
}
99-
100-
private def resetState(): Unit = {
101-
objectClassInfo = null
102-
workTracker = null
103-
_errors.clear()
104-
classLoader = null
105-
_topLevelExportInfos.clear()
136+
.map(_ => postLoad(moduleInitializers))
106137
}
107138

108139
private def loadObjectClass(onSuccess: () => Unit): Unit = {
@@ -154,8 +185,9 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
154185
reachInitializers(moduleInitializers)
155186
}
156187

157-
private def postLoad(moduleInitializers: Seq[ModuleInitializer],
158-
logger: Logger): Analysis = {
188+
private def postLoad(moduleInitializers: Seq[ModuleInitializer]): Unit = {
189+
_classInfos = classLoader.loadedInfos()
190+
159191
if (isNoModule) {
160192
// Check there is only a single module.
161193
val publicModuleIDs = (
@@ -167,51 +199,8 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
167199
_errors ::= MultiplePublicModulesWithoutModuleSupport(publicModuleIDs)
168200
}
169201

170-
val infos = classLoader.loadedInfos()
171-
172202
// Reach additional data, based on reflection methods used
173-
reachDataThroughReflection(infos)
174-
175-
val errs = _errors.get()
176-
177-
if (failOnError && errs.nonEmpty)
178-
reportErrors(logger)
179-
180-
new Analysis {
181-
val classInfos = infos
182-
val topLevelExportInfos = _topLevelExportInfos
183-
val errors = errs
184-
}
185-
}
186-
187-
private def reportErrors(logger: Logger): Unit = {
188-
val errors = _errors.get()
189-
190-
require(errors.nonEmpty)
191-
192-
val maxDisplayErrors = {
193-
val propName = "org.scalajs.linker.maxlinkingerrors"
194-
Try(System.getProperty(propName, "20").toInt).getOrElse(20).max(1)
195-
}
196-
197-
errors
198-
.take(maxDisplayErrors)
199-
.foreach(logError(_, logger, Level.Error))
200-
201-
val skipped = errors.size - maxDisplayErrors
202-
if (skipped > 0)
203-
logger.log(Level.Error, s"Not showing $skipped more linking errors")
204-
205-
if (initial) {
206-
throw new LinkingException("There were linking errors")
207-
} else {
208-
throw new AssertionError(
209-
"There were linking errors after the optimizer has run. " +
210-
"This is a bug, please report it. " +
211-
"You can work around the bug by disabling the optimizer. " +
212-
"In the sbt plugin, this can be done with " +
213-
"`scalaJSLinkerConfig ~= { _.withOptimizer(false) }`.")
214-
}
203+
reachDataThroughReflection()
215204
}
216205

217206
private def reachSymbolRequirement(requirement: SymbolRequirement): Unit = {
@@ -304,10 +293,9 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
304293
}
305294

306295
/** Reach additional class data based on reflection methods being used. */
307-
private def reachDataThroughReflection(
308-
classInfos: scala.collection.Map[ClassName, ClassInfo]): Unit = {
296+
private def reachDataThroughReflection(): Unit = {
309297

310-
val classClassInfo = classInfos.get(ClassClass)
298+
val classClassInfo = _classInfos.get(ClassClass)
311299

312300
/* If Class.getSuperclass() is reachable, we can reach the data of all
313301
* superclasses of classes whose data we can already reach.
@@ -320,7 +308,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
320308
// calledFrom should always be nonEmpty if isReachable, but let's be robust
321309
implicit val from =
322310
getSuperclassMethodInfo.calledFrom.headOption.getOrElse(fromAnalyzer)
323-
for (classInfo <- classInfos.values.filter(_.isDataAccessed).toList) {
311+
for (classInfo <- _classInfos.values.filter(_.isDataAccessed).toList) {
324312
@tailrec
325313
def loop(classInfo: ClassInfo): Unit = {
326314
classInfo.accessData()
@@ -339,8 +327,6 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
339327

340328
private def lookupClass(className: ClassName)(
341329
onSuccess: ClassInfo => Unit)(implicit from: From): Unit = {
342-
implicit val ec = workTracker.ec
343-
344330
workTracker.track {
345331
classLoader.lookupClass(className).map {
346332
case info: ClassInfo =>
@@ -887,8 +873,6 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
887873
createReflProxy(proxyName, onlyCandidate.methodName).reach(this)
888874

889875
case _ =>
890-
implicit val ec = workTracker.ec
891-
892876
val future = for {
893877
reflectiveTarget <- computeMostSpecificProxyMatch(candidates)
894878
} yield {
@@ -939,8 +923,6 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
939923
private def computeMostSpecificProxyMatch(candidates: List[MethodInfo])(
940924
implicit from: From): Future[MethodInfo] = {
941925

942-
implicit val ec = workTracker.ec
943-
944926
/* From the JavaDoc of java.lang.Class.getMethod:
945927
*
946928
* If more than one [candidate] method is found in C, and one of these
@@ -1584,11 +1566,11 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
15841566

15851567
}
15861568

1587-
object Analyzer {
1569+
private object AnalyzerRun {
15881570
private val getSuperclassMethodName =
15891571
MethodName("getSuperclass", Nil, ClassRef(ClassClass))
15901572

1591-
private class WorkTracker(implicit val ec: ExecutionContext) {
1573+
private class WorkTracker(implicit ec: ExecutionContext) {
15921574
private val pending = new AtomicInteger(0)
15931575
@volatile private var _allowComplete = false
15941576
private val promise = Promise[Unit]()

0 commit comments

Comments
 (0)