Skip to content

Commit b18b744

Browse files
committed
faster class dependency cache
1 parent 6a6ee08 commit b18b744

File tree

2 files changed

+51
-28
lines changed

2 files changed

+51
-28
lines changed

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import xsbti.UseScope
2626
import xsbti.api.DependencyContext
2727
import xsbti.api.DependencyContext._
2828

29+
import scala.jdk.CollectionConverters.*
30+
2931
import scala.collection.{Set, mutable}
3032

3133

@@ -74,7 +76,11 @@ class ExtractDependencies extends Phase {
7476
collector.traverse(unit.tpdTree)
7577

7678
if (ctx.settings.YdumpSbtInc.value) {
77-
val deps = rec.classDependencies.map(_.toString).toArray[Object]
79+
val deps = rec.classDependencies.flatMap((k,vs) =>
80+
vs.iterator.flatMap((to, depCtxs) =>
81+
depCtxs.asScala.map(depCtx => s"ClassDependency($k, $to, $depCtx)")
82+
)
83+
).toArray[Object]
7884
val names = rec.usedNames.map { case (clazz, names) => s"$clazz: $names" }.toArray[Object]
7985
Arrays.sort(deps)
8086
Arrays.sort(names)
@@ -265,7 +271,7 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
265271
// Avoid cycles by remembering both the types (testcase:
266272
// tests/run/enum-values.scala) and the symbols of named types (testcase:
267273
// tests/pos-java-interop/i13575) we've seen before.
268-
val seen = new mutable.HashSet[Symbol | Type]
274+
val seen = new util.HashSet[Symbol | Type](64)
269275
def traverse(tp: Type): Unit = if (!seen.contains(tp)) {
270276
seen += tp
271277
tp match {
@@ -306,7 +312,15 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
306312
}
307313
}
308314

309-
case class ClassDependency(fromClass: Symbol, toClass: Symbol, context: DependencyContext)
315+
class ClassDepsInClass:
316+
private val _classes = util.EqHashMap[Symbol, EnumSet[DependencyContext]]()
317+
318+
def addDependency(fromClass: Symbol, context: DependencyContext): Unit =
319+
val set = _classes.getOrElseUpdate(fromClass, EnumSet.noneOf(classOf[DependencyContext]))
320+
set.add(context)
321+
322+
def iterator: Iterator[(Symbol, EnumSet[DependencyContext])] =
323+
_classes.iterator
310324

311325
/** Record dependencies using `addUsedName`/`addClassDependency` and inform Zinc using `sendToZinc()`.
312326
*
@@ -355,10 +369,9 @@ class DependencyRecorder {
355369
* safely.
356370
*/
357371
def addUsedRawName(name: Name, includeSealedChildren: Boolean = false)(using Context): Unit = {
358-
val fromClass = resolveDependencySource
372+
val fromClass = resolveDependencyFromClass
359373
if (fromClass.exists) {
360-
val usedName = _usedNames.getOrElseUpdate(fromClass, new UsedNamesInClass)
361-
usedName.update(name, includeSealedChildren)
374+
lastUsedCache.update(name, includeSealedChildren)
362375
}
363376
}
364377

@@ -373,9 +386,9 @@ class DependencyRecorder {
373386
* of the associated value, see the documentation of parameter `includeSealedChildren`
374387
* of `addUsedRawName`.
375388
*/
376-
private val _names = new mutable.HashMap[Name, DefaultScopes.type | PatMatScopes.type]
389+
private val _names = new util.HashMap[Name, DefaultScopes.type | PatMatScopes.type]
377390

378-
def names: collection.Map[Name, EnumSet[UseScope]] = _names
391+
def iterator: Iterator[(Name, EnumSet[UseScope])] = _names.iterator
379392

380393
private[DependencyRecorder] def update(name: Name, includeSealedChildren: Boolean): Unit = {
381394
if (includeSealedChildren)
@@ -386,7 +399,7 @@ class DependencyRecorder {
386399

387400
override def toString(): String = {
388401
val builder = new StringBuilder
389-
names.foreach { case (name, scopes) =>
402+
iterator.foreach { (name, scopes) =>
390403
builder.append(name.mangledString)
391404
builder.append(" in [")
392405
scopes.forEach(scope => builder.append(scope.toString))
@@ -398,17 +411,17 @@ class DependencyRecorder {
398411
}
399412

400413

401-
private val _classDependencies = new mutable.HashSet[ClassDependency]
414+
private val _classDependencies = new mutable.HashMap[Symbol, ClassDepsInClass]
402415

403-
def classDependencies: Set[ClassDependency] = _classDependencies
416+
def classDependencies: collection.Map[Symbol, ClassDepsInClass] = _classDependencies
404417

405418
/** Record a dependency to the class `to` in a given `context`
406419
* from the current non-local enclosing class.
407420
*/
408421
def addClassDependency(toClass: Symbol, context: DependencyContext)(using Context): Unit =
409-
val fromClass = resolveDependencySource
422+
val fromClass = resolveDependencyFromClass
410423
if (fromClass.exists)
411-
_classDependencies += ClassDependency(fromClass, toClass, context)
424+
lastDepCache.addDependency(toClass, context)
412425

413426
private val _usedNames = new mutable.HashMap[Symbol, UsedNamesInClass]
414427

@@ -418,11 +431,13 @@ class DependencyRecorder {
418431
usedNames.foreach:
419432
case (clazz, usedNames) =>
420433
val className = classNameAsString(clazz)
421-
usedNames.names.foreach:
422-
case (usedName, scopes) =>
423-
cb.usedName(className, usedName.toString, scopes)
434+
usedNames.iterator.foreach: (usedName, scopes) =>
435+
cb.usedName(className, usedName.toString, scopes)
424436
val siblingClassfiles = new mutable.HashMap[PlainFile, Path]
425-
classDependencies.foreach(recordClassDependency(cb, _, siblingClassfiles))
437+
for (fromClass, partialDependencies) <- _classDependencies do
438+
for (toClass, deps) <- partialDependencies.iterator do
439+
for dep <- deps.asScala do
440+
recordClassDependency(cb, fromClass, toClass, dep, siblingClassfiles)
426441
clear()
427442

428443
/** Clear all state. */
@@ -431,15 +446,17 @@ class DependencyRecorder {
431446
_classDependencies.clear()
432447
lastOwner = NoSymbol
433448
lastDepSource = NoSymbol
449+
lastDepCache = null
450+
lastUsedCache = null
434451
_responsibleForImports = NoSymbol
435452

436453
/** Handles dependency on given symbol by trying to figure out if represents a term
437454
* that is coming from either source code (not necessarily compiled in this compilation
438455
* run) or from class file and calls respective callback method.
439456
*/
440-
private def recordClassDependency(cb: interfaces.IncrementalCallback, dep: ClassDependency,
441-
siblingClassfiles: mutable.Map[PlainFile, Path])(using Context): Unit = {
442-
val fromClassName = classNameAsString(dep.fromClass)
457+
private def recordClassDependency(cb: interfaces.IncrementalCallback, fromClass: Symbol, toClass: Symbol,
458+
depCtx: DependencyContext, siblingClassfiles: mutable.Map[PlainFile, Path])(using Context): Unit = {
459+
val fromClassName = classNameAsString(fromClass)
443460
val sourceFile = ctx.compilationUnit.source
444461

445462
/**For a `.tasty` file, constructs a sibling class to the `jpath`.
@@ -465,13 +482,13 @@ class DependencyRecorder {
465482
})
466483

467484
def binaryDependency(path: Path, binaryClassName: String) =
468-
cb.binaryDependency(path, binaryClassName, fromClassName, sourceFile, dep.context)
485+
cb.binaryDependency(path, binaryClassName, fromClassName, sourceFile, depCtx)
469486

470-
val depClass = dep.toClass
487+
val depClass = toClass
471488
val depFile = depClass.associatedFile
472489
if depFile != null then {
473490
// Cannot ignore inheritance relationship coming from the same source (see sbt/zinc#417)
474-
def allowLocal = dep.context == DependencyByInheritance || dep.context == LocalDependencyByInheritance
491+
def allowLocal = depCtx == DependencyByInheritance || depCtx == LocalDependencyByInheritance
475492
val isTasty = depFile.hasTastyExtension
476493

477494
def processExternalDependency() = {
@@ -485,7 +502,7 @@ class DependencyRecorder {
485502
case pf: PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem
486503
binaryDependency(if isTasty then cachedSiblingClass(pf) else pf.jpath, binaryClassName)
487504
case _ =>
488-
internalError(s"Ignoring dependency $depFile of unknown class ${depFile.getClass}}", dep.fromClass.srcPos)
505+
internalError(s"Ignoring dependency $depFile of unknown class ${depFile.getClass}}", fromClass.srcPos)
489506
}
490507
}
491508

@@ -495,23 +512,30 @@ class DependencyRecorder {
495512
// We cannot ignore dependencies coming from the same source file because
496513
// the dependency info needs to propagate. See source-dependencies/trait-trait-211.
497514
val toClassName = classNameAsString(depClass)
498-
cb.classDependency(toClassName, fromClassName, dep.context)
515+
cb.classDependency(toClassName, fromClassName, depCtx)
499516
}
500517
}
501518

502519
private var lastOwner: Symbol = _
503520
private var lastDepSource: Symbol = _
521+
private var lastDepCache: ClassDepsInClass | Null = _
522+
private var lastUsedCache: UsedNamesInClass | Null = _
504523

505524
/** The source of the dependency according to `nonLocalEnclosingClass`
506525
* if it exists, otherwise fall back to `responsibleForImports`.
507526
*
508527
* This is backed by a cache which is invalidated when `ctx.owner` changes.
509528
*/
510-
private def resolveDependencySource(using Context): Symbol = {
529+
private def resolveDependencyFromClass(using Context): Symbol = {
530+
import dotty.tools.uncheckedNN
511531
if (lastOwner != ctx.owner) {
512532
lastOwner = ctx.owner
513533
val source = nonLocalEnclosingClass
514-
lastDepSource = if (source.is(PackageClass)) responsibleForImports else source
534+
val fromClass = if (source.is(PackageClass)) responsibleForImports else source
535+
if lastDepSource != fromClass then
536+
lastDepSource = fromClass
537+
lastDepCache = _classDependencies.getOrElseUpdate(fromClass, new ClassDepsInClass)
538+
lastUsedCache = _usedNames.getOrElseUpdate(fromClass, new UsedNamesInClass)
515539
}
516540

517541
lastDepSource

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import annotation.{tailrec, constructorOnly}
2020
import ast.tpd._
2121
import Synthesizer._
2222
import sbt.ExtractDependencies.*
23-
import sbt.ClassDependency
2423
import xsbti.api.DependencyContext._
2524

2625
/** Synthesize terms for special classes */

0 commit comments

Comments
 (0)