@@ -26,6 +26,8 @@ import xsbti.UseScope
26
26
import xsbti .api .DependencyContext
27
27
import xsbti .api .DependencyContext ._
28
28
29
+ import scala .jdk .CollectionConverters .*
30
+
29
31
import scala .collection .{Set , mutable }
30
32
31
33
@@ -74,7 +76,11 @@ class ExtractDependencies extends Phase {
74
76
collector.traverse(unit.tpdTree)
75
77
76
78
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 ]
78
84
val names = rec.usedNames.map { case (clazz, names) => s " $clazz: $names" }.toArray[Object ]
79
85
Arrays .sort(deps)
80
86
Arrays .sort(names)
@@ -265,7 +271,7 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
265
271
// Avoid cycles by remembering both the types (testcase:
266
272
// tests/run/enum-values.scala) and the symbols of named types (testcase:
267
273
// 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 )
269
275
def traverse (tp : Type ): Unit = if (! seen.contains(tp)) {
270
276
seen += tp
271
277
tp match {
@@ -306,7 +312,15 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
306
312
}
307
313
}
308
314
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
310
324
311
325
/** Record dependencies using `addUsedName`/`addClassDependency` and inform Zinc using `sendToZinc()`.
312
326
*
@@ -355,10 +369,9 @@ class DependencyRecorder {
355
369
* safely.
356
370
*/
357
371
def addUsedRawName (name : Name , includeSealedChildren : Boolean = false )(using Context ): Unit = {
358
- val fromClass = resolveDependencySource
372
+ val fromClass = resolveDependencyFromClass
359
373
if (fromClass.exists) {
360
- val usedName = _usedNames.getOrElseUpdate(fromClass, new UsedNamesInClass )
361
- usedName.update(name, includeSealedChildren)
374
+ lastUsedCache.update(name, includeSealedChildren)
362
375
}
363
376
}
364
377
@@ -373,9 +386,9 @@ class DependencyRecorder {
373
386
* of the associated value, see the documentation of parameter `includeSealedChildren`
374
387
* of `addUsedRawName`.
375
388
*/
376
- private val _names = new mutable .HashMap [Name , DefaultScopes .type | PatMatScopes .type ]
389
+ private val _names = new util .HashMap [Name , DefaultScopes .type | PatMatScopes .type ]
377
390
378
- def names : collection. Map [ Name , EnumSet [UseScope ]] = _names
391
+ def iterator : Iterator [( Name , EnumSet [UseScope ]) ] = _names.iterator
379
392
380
393
private [DependencyRecorder ] def update (name : Name , includeSealedChildren : Boolean ): Unit = {
381
394
if (includeSealedChildren)
@@ -386,7 +399,7 @@ class DependencyRecorder {
386
399
387
400
override def toString (): String = {
388
401
val builder = new StringBuilder
389
- names .foreach { case (name, scopes) =>
402
+ iterator .foreach { (name, scopes) =>
390
403
builder.append(name.mangledString)
391
404
builder.append(" in [" )
392
405
scopes.forEach(scope => builder.append(scope.toString))
@@ -398,17 +411,17 @@ class DependencyRecorder {
398
411
}
399
412
400
413
401
- private val _classDependencies = new mutable.HashSet [ ClassDependency ]
414
+ private val _classDependencies = new mutable.HashMap [ Symbol , ClassDepsInClass ]
402
415
403
- def classDependencies : Set [ ClassDependency ] = _classDependencies
416
+ def classDependencies : collection. Map [ Symbol , ClassDepsInClass ] = _classDependencies
404
417
405
418
/** Record a dependency to the class `to` in a given `context`
406
419
* from the current non-local enclosing class.
407
420
*/
408
421
def addClassDependency (toClass : Symbol , context : DependencyContext )(using Context ): Unit =
409
- val fromClass = resolveDependencySource
422
+ val fromClass = resolveDependencyFromClass
410
423
if (fromClass.exists)
411
- _classDependencies += ClassDependency (fromClass, toClass, context)
424
+ lastDepCache.addDependency( toClass, context)
412
425
413
426
private val _usedNames = new mutable.HashMap [Symbol , UsedNamesInClass ]
414
427
@@ -418,11 +431,13 @@ class DependencyRecorder {
418
431
usedNames.foreach:
419
432
case (clazz, usedNames) =>
420
433
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)
424
436
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)
426
441
clear()
427
442
428
443
/** Clear all state. */
@@ -431,15 +446,17 @@ class DependencyRecorder {
431
446
_classDependencies.clear()
432
447
lastOwner = NoSymbol
433
448
lastDepSource = NoSymbol
449
+ lastDepCache = null
450
+ lastUsedCache = null
434
451
_responsibleForImports = NoSymbol
435
452
436
453
/** Handles dependency on given symbol by trying to figure out if represents a term
437
454
* that is coming from either source code (not necessarily compiled in this compilation
438
455
* run) or from class file and calls respective callback method.
439
456
*/
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)
443
460
val sourceFile = ctx.compilationUnit.source
444
461
445
462
/** For a `.tasty` file, constructs a sibling class to the `jpath`.
@@ -465,13 +482,13 @@ class DependencyRecorder {
465
482
})
466
483
467
484
def binaryDependency (path : Path , binaryClassName : String ) =
468
- cb.binaryDependency(path, binaryClassName, fromClassName, sourceFile, dep.context )
485
+ cb.binaryDependency(path, binaryClassName, fromClassName, sourceFile, depCtx )
469
486
470
- val depClass = dep. toClass
487
+ val depClass = toClass
471
488
val depFile = depClass.associatedFile
472
489
if depFile != null then {
473
490
// 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
475
492
val isTasty = depFile.hasTastyExtension
476
493
477
494
def processExternalDependency () = {
@@ -485,7 +502,7 @@ class DependencyRecorder {
485
502
case pf : PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem
486
503
binaryDependency(if isTasty then cachedSiblingClass(pf) else pf.jpath, binaryClassName)
487
504
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)
489
506
}
490
507
}
491
508
@@ -495,23 +512,30 @@ class DependencyRecorder {
495
512
// We cannot ignore dependencies coming from the same source file because
496
513
// the dependency info needs to propagate. See source-dependencies/trait-trait-211.
497
514
val toClassName = classNameAsString(depClass)
498
- cb.classDependency(toClassName, fromClassName, dep.context )
515
+ cb.classDependency(toClassName, fromClassName, depCtx )
499
516
}
500
517
}
501
518
502
519
private var lastOwner : Symbol = _
503
520
private var lastDepSource : Symbol = _
521
+ private var lastDepCache : ClassDepsInClass | Null = _
522
+ private var lastUsedCache : UsedNamesInClass | Null = _
504
523
505
524
/** The source of the dependency according to `nonLocalEnclosingClass`
506
525
* if it exists, otherwise fall back to `responsibleForImports`.
507
526
*
508
527
* This is backed by a cache which is invalidated when `ctx.owner` changes.
509
528
*/
510
- private def resolveDependencySource (using Context ): Symbol = {
529
+ private def resolveDependencyFromClass (using Context ): Symbol = {
530
+ import dotty .tools .uncheckedNN
511
531
if (lastOwner != ctx.owner) {
512
532
lastOwner = ctx.owner
513
533
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 )
515
539
}
516
540
517
541
lastDepSource
0 commit comments