Skip to content

Commit cef16f1

Browse files
committed
Revert "Revert "Port classpath improvements""
This reverts commit a34b7b8.
1 parent 539dd06 commit cef16f1

8 files changed

+167
-119
lines changed

Diff for: compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala

+43-28
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package dotc.classpath
66

77
import java.net.URL
88
import scala.collection.mutable.ArrayBuffer
9-
import dotty.tools.io.{ AbstractFile, ClassPath, ClassRepresentation }
9+
import scala.collection.immutable.ArraySeq
10+
11+
import dotty.tools.io.{ AbstractFile, ClassPath, ClassRepresentation, EfficientClassPath }
1012

1113
/**
1214
* A classpath unifying multiple class- and sourcepath entries.
@@ -19,20 +21,20 @@ import dotty.tools.io.{ AbstractFile, ClassPath, ClassRepresentation }
1921
case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
2022
override def findClassFile(className: String): Option[AbstractFile] = {
2123
val (pkg, _) = PackageNameUtils.separatePkgAndClassNames(className)
22-
aggregatesForPackage(pkg).iterator.map(_.findClassFile(className)).collectFirst {
24+
aggregatesForPackage(PackageName(pkg)).iterator.map(_.findClassFile(className)).collectFirst {
2325
case Some(x) => x
2426
}
2527
}
2628
private val packageIndex: collection.mutable.Map[String, Seq[ClassPath]] = collection.mutable.Map()
27-
private def aggregatesForPackage(pkg: String): Seq[ClassPath] = packageIndex.synchronized {
28-
packageIndex.getOrElseUpdate(pkg, aggregates.filter(_.hasPackage(pkg)))
29+
private def aggregatesForPackage(pkg: PackageName): Seq[ClassPath] = packageIndex.synchronized {
30+
packageIndex.getOrElseUpdate(pkg.dottedString, aggregates.filter(_.hasPackage(pkg)))
2931
}
3032

3133
override def findClass(className: String): Option[ClassRepresentation] = {
3234
val (pkg, _) = PackageNameUtils.separatePkgAndClassNames(className)
3335

3436
def findEntry(isSource: Boolean): Option[ClassRepresentation] =
35-
aggregatesForPackage(pkg).iterator.map(_.findClass(className)).collectFirst {
37+
aggregatesForPackage(PackageName(pkg)).iterator.map(_.findClass(className)).collectFirst {
3638
case Some(s: SourceFileEntry) if isSource => s
3739
case Some(s: ClassFileEntry) if !isSource => s
3840
}
@@ -53,31 +55,47 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
5355

5456
override def asSourcePathString: String = ClassPath.join(aggregates map (_.asSourcePathString): _*)
5557

56-
override private[dotty] def packages(inPackage: String): Seq[PackageEntry] = {
58+
override private[dotty] def packages(inPackage: PackageName): Seq[PackageEntry] = {
5759
val aggregatedPackages = aggregates.flatMap(_.packages(inPackage)).distinct
5860
aggregatedPackages
5961
}
6062

61-
override private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] =
63+
override private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] =
6264
getDistinctEntries(_.classes(inPackage))
6365

64-
override private[dotty] def sources(inPackage: String): Seq[SourceFileEntry] =
66+
override private[dotty] def sources(inPackage: PackageName): Seq[SourceFileEntry] =
6567
getDistinctEntries(_.sources(inPackage))
6668

67-
override private[dotty] def hasPackage(pkg: String): Boolean = aggregates.exists(_.hasPackage(pkg))
68-
override private[dotty] def list(inPackage: String): ClassPathEntries = {
69-
val (packages, classesAndSources) = aggregates.map { cp =>
70-
try
71-
cp.list(inPackage).toTuple
72-
catch {
69+
override private[dotty] def hasPackage(pkg: PackageName): Boolean = aggregates.exists(_.hasPackage(pkg))
70+
override private[dotty] def list(inPackage: PackageName): ClassPathEntries = {
71+
val packages: java.util.HashSet[PackageEntry] = new java.util.HashSet[PackageEntry]()
72+
val classesAndSourcesBuffer = collection.mutable.ArrayBuffer[ClassRepresentation]()
73+
val onPackage: PackageEntry => Unit = packages.add(_)
74+
val onClassesAndSources: ClassRepresentation => Unit = classesAndSourcesBuffer += _
75+
76+
aggregates.foreach { cp =>
77+
try {
78+
cp match {
79+
case ecp: EfficientClassPath =>
80+
ecp.list(inPackage, onPackage, onClassesAndSources)
81+
case _ =>
82+
val entries = cp.list(inPackage)
83+
entries._1.foreach(entry => packages.add(entry))
84+
classesAndSourcesBuffer ++= entries._2
85+
}
86+
} catch {
7387
case ex: java.io.IOException =>
74-
val e = new FatalError(ex.getMessage)
88+
val e = FatalError(ex.getMessage)
7589
e.initCause(ex)
7690
throw e
7791
}
78-
}.unzip
79-
val distinctPackages = packages.flatten.distinct
80-
val distinctClassesAndSources = mergeClassesAndSources(classesAndSources: _*)
92+
}
93+
94+
val distinctPackages: Seq[PackageEntry] = {
95+
val arr = packages.toArray(new Array[PackageEntry](packages.size()))
96+
ArraySeq.unsafeWrapArray(arr)
97+
}
98+
val distinctClassesAndSources = mergeClassesAndSources(classesAndSourcesBuffer)
8199
ClassPathEntries(distinctPackages, distinctClassesAndSources)
82100
}
83101

@@ -86,19 +104,16 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
86104
* creates an entry containing both of them. If there would be more than one class or source
87105
* entries for the same class it always would use the first entry of each type found on a classpath.
88106
*/
89-
private def mergeClassesAndSources(entries: scala.collection.Seq[ClassRepresentation]*): Seq[ClassRepresentation] = {
107+
private def mergeClassesAndSources(entries: scala.collection.Seq[ClassRepresentation]): Seq[ClassRepresentation] = {
90108
// based on the implementation from MergedClassPath
91109
var count = 0
92-
val indices = collection.mutable.HashMap[String, Int]()
93-
val mergedEntries = new ArrayBuffer[ClassRepresentation](1024)
94-
110+
val indices = new collection.mutable.HashMap[String, Int]()
111+
val mergedEntries = new ArrayBuffer[ClassRepresentation](entries.size)
95112
for {
96-
partOfEntries <- entries
97-
entry <- partOfEntries
98-
}
99-
{
113+
entry <- entries
114+
} {
100115
val name = entry.name
101-
if (indices contains name) {
116+
if (indices.contains(name)) {
102117
val index = indices(name)
103118
val existing = mergedEntries(index)
104119

@@ -113,7 +128,7 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
113128
count += 1
114129
}
115130
}
116-
mergedEntries.toIndexedSeq
131+
if (mergedEntries.isEmpty) Nil else mergedEntries.toIndexedSeq
117132
}
118133

119134
private def getDistinctEntries[EntryType <: ClassRepresentation](getEntries: ClassPath => Seq[EntryType]): Seq[EntryType] = {

Diff for: compiler/src/dotty/tools/dotc/classpath/ClassPath.scala

+21-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ case class ClassPathEntries(packages: scala.collection.Seq[PackageEntry], classe
1010
def toTuple: (scala.collection.Seq[PackageEntry], scala.collection.Seq[ClassRepresentation]) = (packages, classesAndSources)
1111
}
1212

13+
object ClassPathEntries {
14+
val empty = ClassPathEntries(Seq.empty, Seq.empty)
15+
}
16+
1317
trait ClassFileEntry extends ClassRepresentation {
1418
def file: AbstractFile
1519
}
@@ -18,6 +22,21 @@ trait SourceFileEntry extends ClassRepresentation {
1822
def file: AbstractFile
1923
}
2024

25+
case class PackageName(dottedString: String) {
26+
def isRoot: Boolean = dottedString.isEmpty
27+
val dirPathTrailingSlash: String = FileUtils.dirPath(dottedString) + "/"
28+
29+
def entryName(entry: String): String = {
30+
if (isRoot) entry else {
31+
val builder = new java.lang.StringBuilder(dottedString.length + 1 + entry.length)
32+
builder.append(dottedString)
33+
builder.append('.')
34+
builder.append(entry)
35+
builder.toString
36+
}
37+
}
38+
}
39+
2140
trait PackageEntry {
2241
def name: String
2342
}
@@ -50,10 +69,10 @@ private[dotty] case class PackageEntryImpl(name: String) extends PackageEntry
5069

5170
private[dotty] trait NoSourcePaths {
5271
def asSourcePathString: String = ""
53-
private[dotty] def sources(inPackage: String): Seq[SourceFileEntry] = Seq.empty
72+
private[dotty] def sources(inPackage: PackageName): Seq[SourceFileEntry] = Seq.empty
5473
}
5574

5675
private[dotty] trait NoClassPaths {
5776
def findClassFile(className: String): Option[AbstractFile] = None
58-
private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = Seq.empty
77+
private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] = Seq.empty
5978
}

Diff for: compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala

+35-38
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import java.io.{File => JFile}
77
import java.net.URL
88
import java.nio.file.{FileSystems, Files}
99

10-
import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation}
10+
import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation, EfficientClassPath}
1111
import FileUtils._
12+
1213
import scala.collection.JavaConverters._
14+
import scala.collection.immutable.ArraySeq
1315

1416
/**
1517
* A trait allowing to look for classpath entries in directories. It provides common logic for
@@ -18,7 +20,7 @@ import scala.collection.JavaConverters._
1820
* when we have a name of a package.
1921
* It abstracts over the file representation to work with both JFile and AbstractFile.
2022
*/
21-
trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath {
23+
trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends EfficientClassPath {
2224
type F
2325

2426
val dir: F
@@ -33,27 +35,24 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath {
3335
protected def createFileEntry(file: AbstractFile): FileEntryType
3436
protected def isMatchingFile(f: F): Boolean
3537

36-
private def getDirectory(forPackage: String): Option[F] =
37-
if (forPackage == ClassPath.RootPackage)
38+
private def getDirectory(forPackage: PackageName): Option[F] =
39+
if (forPackage.isRoot)
3840
Some(dir)
39-
else {
40-
val packageDirName = FileUtils.dirPath(forPackage)
41-
getSubDir(packageDirName)
42-
}
41+
else
42+
getSubDir(forPackage.dirPathTrailingSlash)
4343

44-
override private[dotty] def hasPackage(pkg: String): Boolean = getDirectory(pkg).isDefined
44+
override private[dotty] def hasPackage(pkg: PackageName): Boolean = getDirectory(pkg).isDefined
4545

46-
private[dotty] def packages(inPackage: String): Seq[PackageEntry] = {
46+
private[dotty] def packages(inPackage: PackageName): Seq[PackageEntry] = {
4747
val dirForPackage = getDirectory(inPackage)
4848
val nestedDirs: Array[F] = dirForPackage match {
4949
case None => emptyFiles
5050
case Some(directory) => listChildren(directory, Some(isPackage))
5151
}
52-
val prefix = PackageNameUtils.packagePrefix(inPackage)
53-
nestedDirs.toIndexedSeq.map(f => PackageEntryImpl(prefix + getName(f)))
52+
ArraySeq.unsafeWrapArray(nestedDirs).map(f => PackageEntryImpl(inPackage.entryName(getName(f))))
5453
}
5554

56-
protected def files(inPackage: String): Seq[FileEntryType] = {
55+
protected def files(inPackage: PackageName): Seq[FileEntryType] = {
5756
val dirForPackage = getDirectory(inPackage)
5857
val files: Array[F] = dirForPackage match {
5958
case None => emptyFiles
@@ -62,21 +61,18 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath {
6261
files.iterator.map(f => createFileEntry(toAbstractFile(f))).toSeq
6362
}
6463

65-
private[dotty] def list(inPackage: String): ClassPathEntries = {
64+
override def list(inPackage: PackageName, onPackageEntry: PackageEntry => Unit, onClassesAndSources: ClassRepresentation => Unit): Unit = {
6665
val dirForPackage = getDirectory(inPackage)
67-
val files: Array[F] = dirForPackage match {
68-
case None => emptyFiles
69-
case Some(directory) => listChildren(directory)
66+
dirForPackage match {
67+
case None =>
68+
case Some(directory) =>
69+
for (file <- listChildren(directory)) {
70+
if (isPackage(file))
71+
onPackageEntry(PackageEntryImpl(inPackage.entryName(getName(file))))
72+
else if (isMatchingFile(file))
73+
onClassesAndSources(createFileEntry(toAbstractFile(file)))
74+
}
7075
}
71-
val packagePrefix = PackageNameUtils.packagePrefix(inPackage)
72-
val packageBuf = collection.mutable.ArrayBuffer.empty[PackageEntry]
73-
val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType]
74-
for (file <- files)
75-
if (isPackage(file))
76-
packageBuf += PackageEntryImpl(packagePrefix + getName(file))
77-
else if (isMatchingFile(file))
78-
fileBuf += createFileEntry(toAbstractFile(file))
79-
ClassPathEntries(packageBuf, fileBuf)
8076
}
8177
}
8278

@@ -159,24 +155,25 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
159155
}
160156

161157
/** Empty string represents root package */
162-
override private[dotty] def hasPackage(pkg: String): Boolean = packageToModuleBases.contains(pkg)
158+
override private[dotty] def hasPackage(pkg: PackageName): Boolean = packageToModuleBases.contains(pkg.dottedString)
163159

164-
override private[dotty] def packages(inPackage: String): Seq[PackageEntry] = {
160+
override private[dotty] def packages(inPackage: PackageName): Seq[PackageEntry] = {
165161
def matches(packageDottedName: String) =
166162
if (packageDottedName.contains("."))
167-
packageOf(packageDottedName) == inPackage
168-
else inPackage == ""
163+
packageOf(packageDottedName) == inPackage.dottedString
164+
else inPackage.isRoot
169165
packageToModuleBases.keysIterator.filter(matches).map(PackageEntryImpl(_)).toVector
170166
}
171-
private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] =
172-
if (inPackage == "") Nil
167+
168+
private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] =
169+
if (inPackage.isRoot) Nil
173170
else
174-
packageToModuleBases.getOrElse(inPackage, Nil).flatMap(x =>
175-
Files.list(x.resolve(FileUtils.dirPath(inPackage))).iterator().asScala.filter(_.getFileName.toString.endsWith(".class"))).map(x =>
171+
packageToModuleBases.getOrElse(inPackage.dottedString, Nil).flatMap(x =>
172+
Files.list(x.resolve(inPackage.dirPathTrailingSlash)).iterator().asScala.filter(_.getFileName.toString.endsWith(".class"))).map(x =>
176173
ClassFileEntryImpl(new PlainFile(new dotty.tools.io.File(x)))).toVector
177174

178-
override private[dotty] def list(inPackage: String): ClassPathEntries =
179-
if (inPackage == "") ClassPathEntries(packages(inPackage), Nil)
175+
override private[dotty] def list(inPackage: PackageName): ClassPathEntries =
176+
if (inPackage.isRoot) ClassPathEntries(packages(inPackage), Nil)
180177
else ClassPathEntries(packages(inPackage), classes(inPackage))
181178

182179
def asURLs: Seq[URL] = Seq(new URL("jrt:/"))
@@ -214,7 +211,7 @@ case class DirectoryClassPath(dir: JFile) extends JFileDirectoryLookup[ClassFile
214211
protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
215212
protected def isMatchingFile(f: JFile): Boolean = f.isClass
216213

217-
private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
214+
private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] = files(inPackage)
218215
}
219216

220217
case class DirectorySourcePath(dir: JFile) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths {
@@ -238,5 +235,5 @@ case class DirectorySourcePath(dir: JFile) extends JFileDirectoryLookup[SourceFi
238235
}
239236
}
240237

241-
private[dotty] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage)
238+
private[dotty] def sources(inPackage: PackageName): Seq[SourceFileEntry] = files(inPackage)
242239
}

Diff for: compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi
4545
Option(lookupPath(dir)(relativePath.split(java.io.File.separator).toIndexedSeq, directory = false))
4646
}
4747

48-
private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
48+
private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] = files(inPackage)
4949

5050
protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
5151
protected def isMatchingFile(f: AbstractFile): Boolean = f.isClass

Diff for: compiler/src/dotty/tools/dotc/classpath/ZipAndJarFileLookupFactory.scala

+10-11
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ object ZipAndJarClassPathFactory extends ZipAndJarFileLookupFactory {
4242

4343
override def findClassFile(className: String): Option[AbstractFile] = {
4444
val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
45-
file(pkg, simpleClassName + ".class").map(_.file)
45+
file(PackageName(pkg), simpleClassName + ".class").map(_.file)
4646
}
4747

4848
// This method is performance sensitive as it is used by SBT's ExtractDependencies phase.
4949
override def findClass(className: String): Option[ClassRepresentation] = {
5050
val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
51-
file(pkg, simpleClassName + ".class")
51+
file(PackageName(pkg), simpleClassName + ".class")
5252
}
5353

54-
override private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
54+
override private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] = files(inPackage)
5555

5656
override protected def createFileEntry(file: FileZipArchive#Entry): ClassFileEntryImpl = ClassFileEntryImpl(file)
5757
override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isClass
@@ -67,7 +67,7 @@ object ZipAndJarClassPathFactory extends ZipAndJarFileLookupFactory {
6767
private case class ManifestResourcesClassPath(file: ManifestResources) extends ClassPath with NoSourcePaths {
6868
override def findClassFile(className: String): Option[AbstractFile] = {
6969
val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
70-
classes(pkg).find(_.name == simpleClassName).map(_.file)
70+
classes(PackageName(pkg)).find(_.name == simpleClassName).map(_.file)
7171
}
7272

7373
override def asClassPathStrings: Seq[String] = Seq(file.path)
@@ -118,21 +118,20 @@ object ZipAndJarClassPathFactory extends ZipAndJarFileLookupFactory {
118118
packages
119119
}
120120

121-
override private[dotty] def packages(inPackage: String): Seq[PackageEntry] = cachedPackages.get(inPackage) match {
121+
override private[dotty] def packages(inPackage: PackageName): Seq[PackageEntry] = cachedPackages.get(inPackage.dottedString) match {
122122
case None => Seq.empty
123123
case Some(PackageFileInfo(_, subpackages)) =>
124-
val prefix = PackageNameUtils.packagePrefix(inPackage)
125-
subpackages.map(packageFile => PackageEntryImpl(prefix + packageFile.name))
124+
subpackages.map(packageFile => PackageEntryImpl(inPackage.entryName(packageFile.name)))
126125
}
127126

128-
override private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = cachedPackages.get(inPackage) match {
127+
override private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] = cachedPackages.get(inPackage.dottedString) match {
129128
case None => Seq.empty
130129
case Some(PackageFileInfo(pkg, _)) =>
131130
(for (file <- pkg if file.isClass) yield ClassFileEntryImpl(file)).toSeq
132131
}
133132

134-
override private[dotty] def hasPackage(pkg: String) = cachedPackages.contains(pkg)
135-
override private[dotty] def list(inPackage: String): ClassPathEntries = ClassPathEntries(packages(inPackage), classes(inPackage))
133+
override private[dotty] def hasPackage(pkg: PackageName) = cachedPackages.contains(pkg.dottedString)
134+
override private[dotty] def list(inPackage: PackageName): ClassPathEntries = ClassPathEntries(packages(inPackage), classes(inPackage))
136135
}
137136

138137
private object ManifestResourcesClassPath {
@@ -164,7 +163,7 @@ object ZipAndJarSourcePathFactory extends ZipAndJarFileLookupFactory {
164163

165164
override def asSourcePathString: String = asClassPathString
166165

167-
override private[dotty] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage)
166+
override private[dotty] def sources(inPackage: PackageName): Seq[SourceFileEntry] = files(inPackage)
168167

169168
override protected def createFileEntry(file: FileZipArchive#Entry): SourceFileEntryImpl = SourceFileEntryImpl(file)
170169
override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isScalaOrJavaSource

0 commit comments

Comments
 (0)