From 6cb50acfb5ee4df342e83d8505116d4607f45d1c Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 22 Mar 2016 21:25:35 +0100 Subject: Enable flat classpath by default Implements VirtualDirectoryFlatClassPath, which is required for the presentation compiler created for the repl's tab-completion. Various minor cleanups in the flat classpath implementation. --- .../nsc/classpath/AggregateFlatClassPath.scala | 10 +- .../nsc/classpath/DirectoryFlatClassPath.scala | 148 +++++++++------------ .../scala/tools/nsc/classpath/FileUtils.scala | 6 +- .../scala/tools/nsc/classpath/FlatClassPath.scala | 9 +- .../tools/nsc/classpath/FlatClassPathFactory.scala | 25 ++-- .../classpath/VirtualDirectoryFlatClassPath.scala | 39 ++++++ .../nsc/classpath/ZipAndJarFileLookupFactory.scala | 8 +- .../tools/nsc/classpath/ZipArchiveFileLookup.scala | 2 +- 8 files changed, 137 insertions(+), 110 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/classpath/VirtualDirectoryFlatClassPath.scala (limited to 'src/compiler/scala/tools/nsc/classpath') diff --git a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala index 3f06264e3c..7bfb3240eb 100644 --- a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala @@ -18,7 +18,6 @@ import scala.tools.nsc.util.ClassRepresentation * @param aggregates classpath instances containing entries which this class processes */ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatClassPath { - override def findClassFile(className: String): Option[AbstractFile] = { @tailrec def find(aggregates: Seq[FlatClassPath]): Option[AbstractFile] = @@ -37,8 +36,7 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl @tailrec def findEntry[T <: ClassRepClassPathEntry](aggregates: Seq[FlatClassPath], getEntries: FlatClassPath => Seq[T]): Option[T] = if (aggregates.nonEmpty) { - val entry = getEntries(aggregates.head) - .find(_.name == simpleClassName) + val entry = getEntries(aggregates.head).find(_.name == simpleClassName) if (entry.isDefined) entry else findEntry(aggregates.tail, getEntries) } else None @@ -46,7 +44,11 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl val classEntry = findEntry(aggregates, classesGetter(pkg)) val sourceEntry = findEntry(aggregates, sourcesGetter(pkg)) - mergeClassesAndSources(classEntry.toList, sourceEntry.toList).headOption + (classEntry, sourceEntry) match { + case (Some(c), Some(s)) => Some(ClassAndSourceFilesEntry(c.file, s.file)) + case (c @ Some(_), _) => c + case (_, s) => s + } } override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs) diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala index 81d2f7320f..e3964dfa78 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala @@ -4,7 +4,6 @@ package scala.tools.nsc.classpath import java.io.File -import java.io.FileFilter import java.net.URL import scala.reflect.io.AbstractFile import scala.reflect.io.PlainFile @@ -12,97 +11,101 @@ import scala.tools.nsc.util.ClassRepresentation import FileUtils._ /** - * A trait allowing to look for classpath entries of given type in directories. - * It provides common logic for classes handling class and source files. + * A trait allowing to look for classpath entries in directories. It provides common logic for + * classes handling class and source files. * It makes use of the fact that in the case of nested directories it's easy to find a file * when we have a name of a package. + * It abstracts over the file representation to work with both JFile and AbstractFile. */ -trait DirectoryFileLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath { - val dir: File - assert(dir != null, "Directory file in DirectoryFileLookup cannot be null") +trait DirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath { + type F + + val dir: F + + protected def emptyFiles: Array[F] // avoids reifying ClassTag[F] + protected def getSubDir(dirName: String): Option[F] + protected def listChildren(dir: F, filter: Option[F => Boolean] = None): Array[F] + protected def getName(f: F): String + protected def toAbstractFile(f: F): AbstractFile + protected def isPackage(f: F): Boolean - override def asURLs: Seq[URL] = Seq(dir.toURI.toURL) - override def asClassPathStrings: Seq[String] = Seq(dir.getPath) + protected def createFileEntry(file: AbstractFile): FileEntryType + protected def isMatchingFile(f: F): Boolean - import FlatClassPath.RootPackage - private def getDirectory(forPackage: String): Option[File] = { - if (forPackage == RootPackage) { + private def getDirectory(forPackage: String): Option[F] = { + if (forPackage == FlatClassPath.RootPackage) { Some(dir) } else { val packageDirName = FileUtils.dirPath(forPackage) - val packageDir = new File(dir, packageDirName) - if (packageDir.exists && packageDir.isDirectory) { - Some(packageDir) - } else None + getSubDir(packageDirName) } } - override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = { + private[nsc] def packages(inPackage: String): Seq[PackageEntry] = { val dirForPackage = getDirectory(inPackage) - val nestedDirs: Array[File] = dirForPackage match { - case None => Array.empty - case Some(directory) => directory.listFiles(DirectoryFileLookup.packageDirectoryFileFilter) + val nestedDirs: Array[F] = dirForPackage match { + case None => emptyFiles + case Some(directory) => listChildren(directory, Some(isPackage)) } val prefix = PackageNameUtils.packagePrefix(inPackage) - val entries = nestedDirs map { file => - PackageEntryImpl(prefix + file.getName) - } - entries + nestedDirs.map(f => PackageEntryImpl(prefix + getName(f))) } protected def files(inPackage: String): Seq[FileEntryType] = { val dirForPackage = getDirectory(inPackage) - val files: Array[File] = dirForPackage match { - case None => Array.empty - case Some(directory) => directory.listFiles(fileFilter) - } - val entries = files map { file => - val wrappedFile = new scala.reflect.io.File(file) - createFileEntry(new PlainFile(wrappedFile)) + val files: Array[F] = dirForPackage match { + case None => emptyFiles + case Some(directory) => listChildren(directory, Some(isMatchingFile)) } - entries + files.map(f => createFileEntry(toAbstractFile(f))) } - override private[nsc] def list(inPackage: String): FlatClassPathEntries = { + private[nsc] def list(inPackage: String): FlatClassPathEntries = { val dirForPackage = getDirectory(inPackage) - val files: Array[File] = dirForPackage match { - case None => Array.empty - case Some(directory) => directory.listFiles() + val files: Array[F] = dirForPackage match { + case None => emptyFiles + case Some(directory) => listChildren(directory) } val packagePrefix = PackageNameUtils.packagePrefix(inPackage) val packageBuf = collection.mutable.ArrayBuffer.empty[PackageEntry] val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType] for (file <- files) { - if (file.isPackage) { - val pkgEntry = PackageEntryImpl(packagePrefix + file.getName) - packageBuf += pkgEntry - } else if (fileFilter.accept(file)) { - val wrappedFile = new scala.reflect.io.File(file) - val abstractFile = new PlainFile(wrappedFile) - fileBuf += createFileEntry(abstractFile) - } + if (isPackage(file)) + packageBuf += PackageEntryImpl(packagePrefix + getName(file)) + else if (isMatchingFile(file)) + fileBuf += createFileEntry(toAbstractFile(file)) } FlatClassPathEntries(packageBuf, fileBuf) } - - protected def createFileEntry(file: AbstractFile): FileEntryType - protected def fileFilter: FileFilter } -object DirectoryFileLookup { +trait JFileDirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends DirectoryLookup[FileEntryType] { + type F = File - private[classpath] object packageDirectoryFileFilter extends FileFilter { - override def accept(pathname: File): Boolean = pathname.isPackage + protected def emptyFiles: Array[File] = Array.empty + protected def getSubDir(packageDirName: String): Option[File] = { + val packageDir = new File(dir, packageDirName) + if (packageDir.exists && packageDir.isDirectory) Some(packageDir) + else None } -} + protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = filter match { + case Some(f) => dir.listFiles(mkFileFilter(f)) + case None => dir.listFiles() + } + protected def getName(f: File): String = f.getName + protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new scala.reflect.io.File(f)) + protected def isPackage(f: File): Boolean = f.isPackage -case class DirectoryFlatClassPath(dir: File) - extends DirectoryFileLookup[ClassFileEntryImpl] - with NoSourcePaths { + assert(dir != null, "Directory file in DirectoryFileLookup cannot be null") + def asURLs: Seq[URL] = Seq(dir.toURI.toURL) + def asClassPathStrings: Seq[String] = Seq(dir.getPath) +} + +case class DirectoryFlatClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl - override def findClassFile(className: String): Option[AbstractFile] = { + def findClassFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) val classFile = new File(s"$dir/$relativePath.class") if (classFile.exists) { @@ -112,31 +115,19 @@ case class DirectoryFlatClassPath(dir: File) } else None } - override protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) - override protected def fileFilter: FileFilter = DirectoryFlatClassPath.classFileFilter - - override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) -} - -object DirectoryFlatClassPath { + protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) + protected def isMatchingFile(f: File): Boolean = f.isClass - private val classFileFilter = new FileFilter { - override def accept(pathname: File): Boolean = pathname.isClass - } + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) } -case class DirectoryFlatSourcePath(dir: File) - extends DirectoryFileLookup[SourceFileEntryImpl] - with NoClassPaths { +case class DirectoryFlatSourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths { + def asSourcePathString: String = asClassPathString - override def asSourcePathString: String = asClassPathString + protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file) + protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName) - override protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file) - override protected def fileFilter: FileFilter = DirectoryFlatSourcePath.sourceFileFilter - - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = { - findSourceFile(className) map SourceFileEntryImpl - } + override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findSourceFile(className) map SourceFileEntryImpl private def findSourceFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) @@ -151,12 +142,5 @@ case class DirectoryFlatSourcePath(dir: File) } } - override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage) -} - -object DirectoryFlatSourcePath { - - private val sourceFileFilter = new FileFilter { - override def accept(pathname: File): Boolean = endsScalaOrJava(pathname.getName) - } + private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage) } diff --git a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala index ee2528e15c..bbcfcb24ca 100644 --- a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala +++ b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala @@ -3,7 +3,7 @@ */ package scala.tools.nsc.classpath -import java.io.{ File => JFile } +import java.io.{File => JFile, FileFilter} import java.net.URL import scala.reflect.internal.FatalError import scala.reflect.io.AbstractFile @@ -65,4 +65,8 @@ object FileUtils { // because then some tests in partest don't pass private def mayBeValidPackage(dirName: String): Boolean = (dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.') + + def mkFileFilter(f: JFile => Boolean) = new FileFilter { + def accept(pathname: JFile): Boolean = f(pathname) + } } diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala index cb201617d2..e95ffe02e3 100644 --- a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala @@ -28,11 +28,8 @@ trait FlatClassPath extends ClassFileLookup[AbstractFile] { override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = { val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) - val foundClassFromClassFiles = classes(pkg) - .find(_.name == simpleClassName) - - def findClassInSources = sources(pkg) - .find(_.name == simpleClassName) + val foundClassFromClassFiles = classes(pkg).find(_.name == simpleClassName) + def findClassInSources = sources(pkg).find(_.name == simpleClassName) foundClassFromClassFiles orElse findClassInSources } @@ -50,7 +47,7 @@ case class FlatClassPathEntries(packages: Seq[PackageEntry], classesAndSources: object FlatClassPathEntries { import scala.language.implicitConversions // to have working unzip method - implicit def entry2Tuple(entry: FlatClassPathEntries) = (entry.packages, entry.classesAndSources) + implicit def entry2Tuple(entry: FlatClassPathEntries): (Seq[PackageEntry], Seq[ClassRepClassPathEntry]) = (entry.packages, entry.classesAndSources) } sealed trait ClassRepClassPathEntry extends ClassRepresentation[AbstractFile] diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala index d8ca325885..463301696e 100644 --- a/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala @@ -3,6 +3,7 @@ */ package scala.tools.nsc.classpath +import scala.reflect.io.VirtualDirectory import scala.tools.nsc.Settings import scala.tools.nsc.io.AbstractFile import FileUtils.AbstractFileOps @@ -12,16 +13,9 @@ import FileUtils.AbstractFileOps * it uses proper type of classpath depending on a types of particular files containing sources or classes. */ class FlatClassPathFactory(settings: Settings) extends ClassPathFactory[FlatClassPath] { + def newClassPath(file: AbstractFile): FlatClassPath = FlatClassPathFactory.newClassPath(file, settings) - override def newClassPath(file: AbstractFile): FlatClassPath = - if (file.isJarOrZip) - ZipAndJarFlatClassPathFactory.create(file, settings) - else if (file.isDirectory) - new DirectoryFlatClassPath(file.file) - else - sys.error(s"Unsupported classpath element: $file") - - override def sourcesInPath(path: String): List[FlatClassPath] = + def sourcesInPath(path: String): List[FlatClassPath] = for { file <- expandPath(path, expandStar = false) dir <- Option(AbstractFile getDirectory file) @@ -35,3 +29,16 @@ class FlatClassPathFactory(settings: Settings) extends ClassPathFactory[FlatClas else sys.error(s"Unsupported sourcepath element: $file") } + +object FlatClassPathFactory { + def newClassPath(file: AbstractFile, settings: Settings): FlatClassPath = file match { + case vd: VirtualDirectory => VirtualDirectoryFlatClassPath(vd) + case _ => + if (file.isJarOrZip) + ZipAndJarFlatClassPathFactory.create(file, settings) + else if (file.isDirectory) + new DirectoryFlatClassPath(file.file) + else + sys.error(s"Unsupported classpath element: $file") + } +} diff --git a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryFlatClassPath.scala new file mode 100644 index 0000000000..06cdab583c --- /dev/null +++ b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryFlatClassPath.scala @@ -0,0 +1,39 @@ +package scala.tools.nsc.classpath + +import scala.tools.nsc.util.ClassRepresentation +import scala.reflect.io.{Path, PlainFile, VirtualDirectory, AbstractFile} +import FileUtils._ +import java.net.URL + +case class VirtualDirectoryFlatClassPath(dir: VirtualDirectory) extends FlatClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { + type F = AbstractFile + + protected def emptyFiles: Array[AbstractFile] = Array.empty + protected def getSubDir(packageDirName: String): Option[AbstractFile] = + Option(dir.lookupName(packageDirName, directory = true)) + protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile => Boolean] = None): Array[F] = filter match { + case Some(f) => dir.iterator.filter(f).toArray + case _ => dir.toArray + } + def getName(f: AbstractFile): String = f.name + def toAbstractFile(f: AbstractFile): AbstractFile = f + def isPackage(f: AbstractFile): Boolean = f.isPackage + + // mimic the behavior of the old nsc.util.DirectoryClassPath + def asURLs: Seq[URL] = Seq(new URL(dir.name)) + def asClassPathStrings: Seq[String] = Seq(dir.path) + + override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl + + def findClassFile(className: String): Option[AbstractFile] = { + val relativePath = FileUtils.dirPath(className) + val classFile = new PlainFile(Path(s"$dir/$relativePath.class")) + if (classFile.exists) Some(classFile) + else None + } + + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) + + protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) + protected def isMatchingFile(f: AbstractFile): Boolean = f.isClass +} diff --git a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala index 85c7c3c843..6ec3805d8b 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala @@ -19,7 +19,6 @@ import FileUtils._ * when there are a lot of projects having a lot of common dependencies. */ sealed trait ZipAndJarFileLookupFactory { - private val cache = collection.mutable.Map.empty[AbstractFile, FlatClassPath] def create(zipFile: AbstractFile, settings: Settings): FlatClassPath = { @@ -44,7 +43,6 @@ sealed trait ZipAndJarFileLookupFactory { * It should be the only way of creating them as it provides caching. */ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { - private case class ZipArchiveFlatClassPath(zipFile: File) extends ZipArchiveFileLookup[ClassFileEntryImpl] with NoSourcePaths { @@ -67,10 +65,7 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { * with a particularly prepared scala-library.jar. It should have all classes listed in the manifest like e.g. this entry: * Name: scala/Function2$mcFJD$sp.class */ - private case class ManifestResourcesFlatClassPath(file: ManifestResources) - extends FlatClassPath - with NoSourcePaths { - + private case class ManifestResourcesFlatClassPath(file: ManifestResources) extends FlatClassPath with NoSourcePaths { override def findClassFile(className: String): Option[AbstractFile] = { val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) classes(pkg).find(_.name == simpleClassName).map(_.file) @@ -163,7 +158,6 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { * It should be the only way of creating them as it provides caching. */ object ZipAndJarFlatSourcePathFactory extends ZipAndJarFileLookupFactory { - private case class ZipArchiveFlatSourcePath(zipFile: File) extends ZipArchiveFileLookup[SourceFileEntryImpl] with NoClassPaths { diff --git a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala index 1d0de57779..a24d989306 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala @@ -57,7 +57,7 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends Flat } getOrElse FlatClassPathEntries(Seq.empty, Seq.empty) } - private def findDirEntry(pkg: String) = { + private def findDirEntry(pkg: String): Option[archive.DirEntry] = { val dirName = s"${FileUtils.dirPath(pkg)}/" archive.allDirs.get(dirName) } -- cgit v1.2.3