Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import dotty.tools.backend.jvm.BTypes.InternalName
import dotty.tools.backend.jvm.opt.*
import dotty.tools.backend.jvm.ClassNode1
import dotty.tools.backend.jvm.analysis.AnalysisUtils.LambdaMetaFactoryCall
import dotty.tools.dotc.classpath.{AggregateClassPath, CtSymClassPath, JrtClassPath}
import dotty.tools.dotc.classpath.{AggregateClassPath, ClassPath, CtSymClassPath, JrtClassPath}
import dotty.tools.io
import dotty.tools.io.ClassPath

import scala.collection.{concurrent, mutable}
import scala.jdk.CollectionConverters.*
Expand Down
63 changes: 4 additions & 59 deletions compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ package dotty.tools
package dotc.classpath

import java.net.URL
import scala.collection.mutable.ArrayBuffer
import dotc.util

import dotty.tools.io.{ AbstractFile, ClassPath, ClassRepresentation }
import dotty.tools.io.AbstractFile

/**
* A classpath unifying multiple class- and sourcepath entries.
Expand All @@ -32,7 +31,7 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {

override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs)

override def packages(inPackage: String): Iterable[PackageEntry] =
override def packages(inPackage: String): Iterable[String] =
aggregates.flatMap(_.packages(inPackage)).distinct

override def classes(inPackage: String): Iterable[BinaryFileEntry] =
Expand All @@ -43,61 +42,7 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {

override def hasPackage(pkg: String): Boolean = aggregates.exists(_.hasPackage(pkg))

/** Returns only one entry for each name.
*
* If there's both a source and a class entry, it
* creates an entry containing both of them. If there would be more than one class or source
* entries for the same class it always would use the first entry of each type found on a classpath.
*
* A TASTy file with no class file entry will be chosen over a class file entry. This can happen if we load
* the Scala 2 library as it has one JAR containing the class files and one JAR containing the TASTy files.
* As classpath orders are not guaranteed to be deterministic we might end up having the TASTy in a later classpath entry.
*/
private def mergeClassesAndSources(entries: scala.collection.Seq[ClassRepresentation]): Seq[ClassRepresentation] = {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead

// based on the implementation from MergedClassPath
var count = 0
val indices = util.HashMap[String, Int]()
val mergedEntries = new ArrayBuffer[ClassRepresentation](entries.size)
for {
entry <- entries
} {
val name = entry.name
if (indices.contains(name)) {
val index = indices(name)
val existing = mergedEntries(index)
(entry, existing) match
case (entry: SourceFileEntry, existing: BinaryFileEntry) =>
mergedEntries(index) = BinaryAndSourceFilesEntry(existing, entry)
case (entry: BinaryFileEntry, existing: SourceFileEntry) =>
mergedEntries(index) = BinaryAndSourceFilesEntry(entry, existing)
case (entry: StandaloneTastyFileEntry, _: ClassFileEntry) =>
// Here we do not create a TastyWithClassFileEntry because the TASTy and the classfile
// come from different classpaths. These may not have the same TASTy UUID.
mergedEntries(index) = entry
case (entry: StandaloneTastyFileEntry, BinaryAndSourceFilesEntry(_: ClassFileEntry, sourceEntry)) =>
mergedEntries(index) = BinaryAndSourceFilesEntry(entry, sourceEntry)
case _ =>
}
else {
indices(name) = count
mergedEntries += entry
count += 1
}
}
if (mergedEntries.isEmpty) Nil else mergedEntries.toIndexedSeq
}

private def getDistinctEntries[EntryType <: ClassRepresentation](getEntries: ClassPath => Iterable[EntryType]): Iterable[EntryType] = {
private def getDistinctEntries[EntryType <: ClassRepresentation](getEntries: ClassPath => Iterable[EntryType]): Iterable[EntryType] =
val seenNames = util.HashSet[String]()
val entriesBuffer = new ArrayBuffer[EntryType](1024)
for {
cp <- aggregates
entry <- getEntries(cp) if !seenNames.contains(entry.name)
}
{
entriesBuffer += entry
seenNames += entry.name
}
entriesBuffer.toIndexedSeq
}
aggregates.flatMap(getEntries).filter(e => seenNames.add(e.name))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was overly convoluted

}
96 changes: 65 additions & 31 deletions compiler/src/dotty/tools/dotc/classpath/ClassPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,81 @@
*/
package dotty.tools.dotc.classpath

import dotty.tools.io.{AbstractFile, ClassRepresentation, FileExtension}

@SolalPirelli SolalPirelli Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes in this file:

  • add ClassPath, unmodified, from io (yes this file is already named ClassPath but it didn't contain ClassPath...)
  • remove subclasses of ClassRepresentation that were unused
    • including making BinaryFileEntry concrete and delete its subclasses since they all did the same thing and were never pattern-matched outside of now-dead code

import dotty.tools.dotc
import dotty.tools.io.File.pathSeparator
import dotty.tools.io.{AbstractFile, Directory, File, FileExtension}

case class PackageEntry(name: String)
import java.net.URL
import java.util.regex.PatternSyntaxException

/** A TASTy file or classfile */
sealed trait BinaryFileEntry extends ClassRepresentation {
def file: AbstractFile
final def fileName: String = file.name
final def name: String = FileUtils.stripExtension(file.name) // class name
final def source: Option[AbstractFile] = None
}
/**
* A representation of the compiler's class- or sourcepath.
*/
trait ClassPath {
def asURLs: Seq[URL] = Seq.empty
def hasPackage(pkg: String): Boolean = false
def packages(inPackage: String): Iterable[String] = Seq.empty
def classes(inPackage: String): Iterable[BinaryFileEntry] = Seq.empty
def sources(inPackage: String): Iterable[SourceFileEntry] = Seq.empty

object BinaryFileEntry {
def apply(file: AbstractFile): BinaryFileEntry =
if file.exists && file.ext.isTasty then
if file.resolveSiblingWithExtension(FileExtension.Class) != null then TastyWithClassFileEntry(file)
else StandaloneTastyFileEntry(file)
else
ClassFileEntry(file)
/**
* Returns *only* the classfile for an external name, e.g., "java.lang.String". This method does not
* return source files or tasty files.
*
* This method is used by the classfile parser. When parsing a Java class, its own inner classes
* are entered with a `ClassfileLoader` that parses the classfile returned by this method.
* It is also used in the backend, by the inliner, to obtain the bytecode when inlining from the
* classpath. It's also used by scalap.
*/
def findClassFile(className: String): Option[AbstractFile] = None
}

/** A classfile or .sig that does not have an associated TASTy file */
private[dotty] final case class ClassFileEntry(file: AbstractFile) extends BinaryFileEntry {
def binary: Option[AbstractFile] = Some(file)
object ClassPath {
val RootPackage: String = ""

/** Expand single path entry */
private def expandS(pattern: String): List[String] = {
val wildSuffix = File.separator + "*"

/* Get all subdirectories, jars, zips out of a directory. */
def lsDir(dir: Directory, filt: String => Boolean = _ => true) =
dir.list.filter(x => filt(x.name) && (x.isDirectory || x.ext.isJarOrZip)).map(_.path).toList

if (pattern == "*") lsDir(Directory("."))
// On Windows the JDK supports forward slash or backslash in classpath entries
else if (pattern.endsWith(wildSuffix) || pattern.endsWith("/*")) lsDir(Directory(pattern dropRight 2))
else if (pattern.contains('*')) {
try {
val regexp = ("^" + pattern.replace("""\*""", """.*""") + "$").r
lsDir(Directory(pattern).parent, regexp.findFirstIn(_).isDefined)
}
catch { case _: PatternSyntaxException => List(pattern) }
}
else List(pattern)
}

/** Split classpath using platform-dependent path separator */
def split(path: String): List[String] = path.split(pathSeparator).toList.filterNot(_ == "").distinct

/** Expand path and possibly expanding stars */
def expandPath(path: String, expandStar: Boolean = true): List[String] =
if (expandStar) split(path).flatMap(expandS)
else split(path)
}

/** A TASTy file that has an associated class file */
private[dotty] final case class TastyWithClassFileEntry(file: AbstractFile) extends BinaryFileEntry {
def binary: Option[AbstractFile] = Some(file)
trait ClassRepresentation {
def fileName: String
def name: String
def binary: Option[AbstractFile]
def source: Option[AbstractFile]
}

/** A TASTy file that does not have an associated class file */
private[dotty] final case class StandaloneTastyFileEntry(file: AbstractFile) extends BinaryFileEntry {
/** A TASTy file or classfile */
private[dotty] final case class BinaryFileEntry(file: AbstractFile) extends ClassRepresentation {
def fileName: String = file.name
def name: String = FileUtils.stripExtension(file.name) // class name
def binary: Option[AbstractFile] = Some(file)
def source: Option[AbstractFile] = None
}

private[dotty] final case class SourceFileEntry(file: AbstractFile) extends ClassRepresentation {
Expand All @@ -45,10 +86,3 @@ private[dotty] final case class SourceFileEntry(file: AbstractFile) extends Clas
def binary: Option[AbstractFile] = None
def source: Option[AbstractFile] = Some(file)
}

private[dotty] final case class BinaryAndSourceFilesEntry(binaryEntry: BinaryFileEntry, sourceEntry: SourceFileEntry) extends ClassRepresentation {
def fileName: String = binaryEntry.fileName
def name: String = binaryEntry.name
def binary: Option[AbstractFile] = binaryEntry.binary
def source: Option[AbstractFile] = sourceEntry.source
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
package dotty.tools.dotc.classpath

import dotty.tools.io.{AbstractFile, ClassPath, Directory, File, Path, VirtualDirectory}
import dotty.tools.io.{AbstractFile, Directory, File, Path, VirtualDirectory}
import dotty.tools.dotc.classpath.FileUtils.isClassContainer
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.interactive.LogicalSourcePath
Expand Down Expand Up @@ -34,7 +34,7 @@ class ClassPathFactory(precomputedSourcePackages: Option[LogicalPackage] = None)
yield ClassPathFactory.newSourcePath(dir)
}

def expandPath(path: String, expandStar: Boolean = true): List[String] = dotty.tools.io.ClassPath.expandPath(path, expandStar)
def expandPath(path: String, expandStar: Boolean = true): List[String] = ClassPath.expandPath(path, expandStar)

/** Expand dir out to contents, a la extdir */
private def expandDir(extdir: String)(using Context): List[String] =
Expand Down
Loading
Loading