@@ -8,7 +8,7 @@ import java.nio.file.Path
8
8
import java .util .{Arrays , EnumSet }
9
9
10
10
import dotty .tools .dotc .ast .tpd
11
- import dotty .tools .dotc .classpath .FileUtils .{isTasty , isClass }
11
+ import dotty .tools .dotc .classpath .FileUtils .{isTasty , hasClassExtension , hasTastyExtension }
12
12
import dotty .tools .dotc .core .Contexts ._
13
13
import dotty .tools .dotc .core .Decorators ._
14
14
import dotty .tools .dotc .core .Flags ._
@@ -21,7 +21,7 @@ import dotty.tools.dotc.core.Types._
21
21
import dotty .tools .dotc .transform .SymUtils ._
22
22
import dotty .tools .dotc .util .{SrcPos , NoSourcePosition }
23
23
import dotty .tools .io
24
- import dotty .tools .io .{AbstractFile , PlainFile , ZipArchive }
24
+ import dotty .tools .io .{AbstractFile , PlainFile , ZipArchive , NoAbstractFile }
25
25
import xsbti .UseScope
26
26
import xsbti .api .DependencyContext
27
27
import xsbti .api .DependencyContext ._
@@ -421,58 +421,81 @@ class DependencyRecorder {
421
421
usedNames.names.foreach:
422
422
case (usedName, scopes) =>
423
423
cb.usedName(className, usedName.toString, scopes)
424
- classDependencies.foreach(recordClassDependency(cb, _))
424
+ val siblingClassfiles = new mutable.HashMap [PlainFile , Path ]
425
+ classDependencies.foreach(recordClassDependency(cb, _, siblingClassfiles))
425
426
clear()
426
427
427
428
/** Clear all state. */
428
- def clear (): Unit =
429
- _usedNames.clear()
430
- _classDependencies.clear()
431
- lastOwner = NoSymbol
432
- lastDepSource = NoSymbol
433
- _responsibleForImports = NoSymbol
429
+ def clear (): Unit =
430
+ _usedNames.clear()
431
+ _classDependencies.clear()
432
+ lastOwner = NoSymbol
433
+ lastDepSource = NoSymbol
434
+ _responsibleForImports = NoSymbol
434
435
435
436
/** Handles dependency on given symbol by trying to figure out if represents a term
436
437
* that is coming from either source code (not necessarily compiled in this compilation
437
438
* run) or from class file and calls respective callback method.
438
439
*/
439
- private def recordClassDependency (cb : interfaces.IncrementalCallback , dep : ClassDependency )(using Context ): Unit = {
440
+ private def recordClassDependency (cb : interfaces.IncrementalCallback , dep : ClassDependency ,
441
+ siblingClassfiles : mutable.Map [PlainFile , Path ])(using Context ): Unit = {
440
442
val fromClassName = classNameAsString(dep.fromClass)
441
443
val sourceFile = ctx.compilationUnit.source
442
444
443
- def binaryDependency (file : Path , binaryClassName : String ) =
444
- cb.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context)
445
-
446
- def processExternalDependency (depFile : AbstractFile , binaryClassName : String ) = {
447
- depFile match {
448
- case ze : ZipArchive # Entry => // The dependency comes from a JAR
449
- ze.underlyingSource match
450
- case Some (zip) if zip.jpath != null =>
451
- binaryDependency(zip.jpath, binaryClassName)
452
- case _ =>
453
- case pf : PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem
454
- binaryDependency(pf.jpath, binaryClassName)
455
- case _ =>
456
- internalError(s " Ignoring dependency $depFile of unknown class ${depFile.getClass}} " , dep.fromClass.srcPos)
457
- }
458
- }
459
-
460
- val depFile = dep.toClass.associatedFile
461
- if (depFile != null ) {
445
+ /** For a `.tasty` file, constructs a sibling class to the `jpath`.
446
+ * Does not validate if it exists as a real file.
447
+ *
448
+ * Because classpath scanning looks for tasty files first, `dep.fromClass` will be
449
+ * associated to a `.tasty` file. However Zinc records all dependencies either based on `.jar` or `.class` files,
450
+ * where classes are in directories on the filesystem.
451
+ *
452
+ * So if the dependency comes from an upstream `.tasty` file and it was not packaged in a jar, then
453
+ * we need to call this to resolve the classfile that will eventually exist at runtime.
454
+ *
455
+ * The way this works is that by the end of compilation analysis,
456
+ * we should have called `cb.generatedNonLocalClass` with the same class file name.
457
+ *
458
+ * FIXME: we still need a way to resolve the correct classfile when we split tasty and classes between
459
+ * different outputs (e.g. stdlib-bootstrapped).
460
+ */
461
+ def cachedSiblingClass (pf : PlainFile ): Path =
462
+ siblingClassfiles.getOrElseUpdate(pf, {
463
+ val jpath = pf.jpath
464
+ jpath.getParent.resolve(jpath.getFileName.toString.stripSuffix(" .tasty" ) + " .class" )
465
+ })
466
+
467
+ def binaryDependency (path : Path , binaryClassName : String ) =
468
+ cb.binaryDependency(path, binaryClassName, fromClassName, sourceFile, dep.context)
469
+
470
+ val depClass = dep.toClass
471
+ val depFile = depClass.associatedFile
472
+ if depFile != null then {
462
473
// Cannot ignore inheritance relationship coming from the same source (see sbt/zinc#417)
463
474
def allowLocal = dep.context == DependencyByInheritance || dep.context == LocalDependencyByInheritance
464
- val depClassFile =
465
- if depFile.isClass then depFile
466
- else depFile.resolveSibling(dep.toClass.binaryClassName + " .class" )
467
- if (depClassFile != null ) {
468
- // Dependency is external -- source is undefined
469
- processExternalDependency(depClassFile, dep.toClass.binaryClassName)
470
- } else if (allowLocal || depFile != sourceFile.file) {
475
+ val isTasty = depFile.hasTastyExtension
476
+
477
+ def processExternalDependency () = {
478
+ val binaryClassName = depClass.binaryClassName
479
+ depFile match {
480
+ case ze : ZipArchive # Entry => // The dependency comes from a JAR
481
+ ze.underlyingSource match
482
+ case Some (zip) if zip.jpath != null =>
483
+ binaryDependency(zip.jpath, binaryClassName)
484
+ case _ =>
485
+ case pf : PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem
486
+ binaryDependency(if isTasty then cachedSiblingClass(pf) else pf.jpath, binaryClassName)
487
+ case _ =>
488
+ internalError(s " Ignoring dependency $depFile of unknown class ${depFile.getClass}} " , dep.fromClass.srcPos)
489
+ }
490
+ }
491
+
492
+ if isTasty || depFile.hasClassExtension then
493
+ processExternalDependency()
494
+ else if allowLocal || depFile != sourceFile.file then
471
495
// We cannot ignore dependencies coming from the same source file because
472
496
// the dependency info needs to propagate. See source-dependencies/trait-trait-211.
473
- val toClassName = classNameAsString(dep.toClass )
497
+ val toClassName = classNameAsString(depClass )
474
498
cb.classDependency(toClassName, fromClassName, dep.context)
475
- }
476
499
}
477
500
}
478
501
0 commit comments