Skip to content

Commit d828648

Browse files
authored
Merge pull request #9644 from dotty-staging/fix-9642
Fix #9642: fix bootstrapping on Windows
2 parents 757e431 + 4c5c00c commit d828648

10 files changed

+176
-120
lines changed

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] = {

compiler/src/dotty/tools/dotc/classpath/ClassPath.scala

+28-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,28 @@ trait SourceFileEntry extends ClassRepresentation {
1822
def file: AbstractFile
1923
}
2024

25+
case class PackageName(dottedString: String) {
26+
val dirPathTrailingSlashJar: String = FileUtils.dirPathInJar(dottedString) + "/"
27+
28+
val dirPathTrailingSlash: String =
29+
if (java.io.File.separatorChar == '/')
30+
dirPathTrailingSlashJar
31+
else
32+
FileUtils.dirPath(dottedString) + java.io.File.separator
33+
34+
def isRoot: Boolean = dottedString.isEmpty
35+
36+
def entryName(entry: String): String = {
37+
if (isRoot) entry else {
38+
val builder = new java.lang.StringBuilder(dottedString.length + 1 + entry.length)
39+
builder.append(dottedString)
40+
builder.append('.')
41+
builder.append(entry)
42+
builder.toString
43+
}
44+
}
45+
}
46+
2147
trait PackageEntry {
2248
def name: String
2349
}
@@ -50,10 +76,10 @@ private[dotty] case class PackageEntryImpl(name: String) extends PackageEntry
5076

5177
private[dotty] trait NoSourcePaths {
5278
def asSourcePathString: String = ""
53-
private[dotty] def sources(inPackage: String): Seq[SourceFileEntry] = Seq.empty
79+
private[dotty] def sources(inPackage: PackageName): Seq[SourceFileEntry] = Seq.empty
5480
}
5581

5682
private[dotty] trait NoClassPaths {
5783
def findClassFile(className: String): Option[AbstractFile] = None
58-
private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = Seq.empty
84+
private[dotty] def classes(inPackage: PackageName): Seq[ClassFileEntry] = Seq.empty
5985
}

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
}

compiler/src/dotty/tools/dotc/classpath/FileUtils.scala

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ object FileUtils {
4444

4545
def dirPath(forPackage: String): String = forPackage.replace('.', JFile.separatorChar)
4646

47+
def dirPathInJar(forPackage: String): String = forPackage.replace('.', '/')
48+
4749
def endsClass(fileName: String): Boolean =
4850
fileName.length > 6 && fileName.substring(fileName.length - 6) == ".class"
4951

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

0 commit comments

Comments
 (0)