summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authormpociecha <michal.pociecha@gmail.com>2014-11-30 15:28:50 +0100
committermpociecha <michal.pociecha@gmail.com>2014-11-30 22:31:32 +0100
commit672c1195c7fee7ca2a8d29402c68a33c167b6966 (patch)
tree701b8b4698f10932de7aa9d9c41db883c37e5568 /src/compiler
parent9fe0c8cc824f938fe4303caa668a5d3f267d1223 (diff)
downloadscala-672c1195c7fee7ca2a8d29402c68a33c167b6966.tar.gz
scala-672c1195c7fee7ca2a8d29402c68a33c167b6966.tar.bz2
scala-672c1195c7fee7ca2a8d29402c68a33c167b6966.zip
Add flat classpath implementation using ManifestResources
There's added the flat classpath type using ManifestResources, closely related to the support for JSR-223 (Scripting for the Java Platform). It uses classes listed in the manifest file placed in the JAR. It's related to jar files so it's created using ZipAndJarFlatClassPathFactory and is cached. In general currently it's not possible to use it in Scala out of the box (without using additional tools such as jarlister) as this support is postponed. The old classpath has been properly prepared in the PR created by @rjolly https://github.com/scala/scala/pull/2238 so the new one also got this feature. ManifestResources is a ZipArchive without a real underlying file placed on a disk and in addition implementing some methods declared in AbstractFile as unsupported operations. Therefore the implementation has to use the iterator. I wanted to have the similar behaviour as in the case of directories and zip/jar files - be able to get a directory entry for a package without iterating all entries. This is achieved by iterating all entries only once and caching packages. This flat classpath type was the last needed one.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala101
1 files changed, 97 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala
index 5096ca6f70..6d8c4880d5 100644
--- a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala
+++ b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala
@@ -4,7 +4,9 @@
package scala.tools.nsc.classpath
import java.io.File
-import scala.reflect.io.{ AbstractFile, FileZipArchive }
+import java.net.URL
+import scala.annotation.tailrec
+import scala.reflect.io.{ AbstractFile, FileZipArchive, ManifestResources }
import scala.tools.nsc.Settings
import FileUtils._
@@ -53,11 +55,102 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory {
override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isClass
}
+ /**
+ * This type of classpath is closly related to the support for JSR-223.
+ * Its usage can be observed e.g. when running:
+ * jrunscript -classpath scala-compiler.jar;scala-reflect.jar;scala-library.jar -l scala
+ * 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 {
+
+ override def findClassFile(className: String): Option[AbstractFile] = {
+ val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
+ classes(pkg).find(_.name == simpleClassName).map(_.file)
+ }
+
+ override def asClassPathStrings: Seq[String] = Seq(file.path)
+
+ override def asURLs: Seq[URL] = file.toURLs()
+
+ import ManifestResourcesFlatClassPath.PackageFileInfo
+ import ManifestResourcesFlatClassPath.PackageInfo
+
+ /**
+ * A cache mapping package name to abstract file for package directory and subpackages of given package.
+ *
+ * ManifestResources can iterate through the collections of entries from e.g. remote jar file.
+ * We can't just specify the path to the concrete directory etc. so we can't just 'jump' into
+ * given package, when it's needed. On the other hand we can iterate over entries to get
+ * AbstractFiles, iterate over entries of these files etc.
+ *
+ * Instead of traversing a tree of AbstractFiles once and caching all entries or traversing each time,
+ * when we need subpackages of a given package or its classes, we traverse once and cache only packages.
+ * Classes for given package can be then easily loaded when they are needed.
+ */
+ private lazy val cachedPackages: collection.mutable.HashMap[String, PackageFileInfo] = {
+ val packages = collection.mutable.HashMap[String, PackageFileInfo]()
+
+ def getSubpackages(dir: AbstractFile): List[AbstractFile] =
+ (for (file <- dir if file.isPackage) yield file)(collection.breakOut)
+
+ @tailrec
+ def traverse(packagePrefix: String,
+ filesForPrefix: List[AbstractFile],
+ subpackagesQueue: collection.mutable.Queue[PackageInfo]): Unit = filesForPrefix match {
+ case pkgFile :: remainingFiles =>
+ val subpackages = getSubpackages(pkgFile)
+ val fullPkgName = packagePrefix + pkgFile.name
+ packages.put(fullPkgName, PackageFileInfo(pkgFile, subpackages))
+ val newPackagePrefix = fullPkgName + "."
+ subpackagesQueue.enqueue(PackageInfo(newPackagePrefix, subpackages))
+ traverse(packagePrefix, remainingFiles, subpackagesQueue)
+ case Nil if subpackagesQueue.nonEmpty =>
+ val PackageInfo(packagePrefix, filesForPrefix) = subpackagesQueue.dequeue()
+ traverse(packagePrefix, filesForPrefix, subpackagesQueue)
+ case _ =>
+ }
+
+ val subpackages = getSubpackages(file)
+ packages.put(FlatClassPath.RootPackage, PackageFileInfo(file, subpackages))
+ traverse(FlatClassPath.RootPackage, subpackages, collection.mutable.Queue())
+ packages
+ }
+
+ override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = cachedPackages.get(inPackage) match {
+ case None => Seq.empty
+ case Some(PackageFileInfo(_, subpackages)) =>
+ val prefix = PackageNameUtils.packagePrefix(inPackage)
+ subpackages.map(packageFile => PackageEntryImpl(prefix + packageFile.name))
+ }
+
+ override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = cachedPackages.get(inPackage) match {
+ case None => Seq.empty
+ case Some(PackageFileInfo(pkg, _)) =>
+ (for (file <- pkg if file.isClass) yield ClassFileEntryImpl(file))(collection.breakOut)
+ }
+
+ override private[nsc] def list(inPackage: String): FlatClassPathEntries = FlatClassPathEntries(packages(inPackage), classes(inPackage))
+ }
+
+ private object ManifestResourcesFlatClassPath {
+ case class PackageFileInfo(packageFile: AbstractFile, subpackages: Seq[AbstractFile])
+ case class PackageInfo(packageName: String, subpackages: List[AbstractFile])
+ }
+
override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath =
- if (zipFile.file == null) {
- val errorMsg = s"Abstract files which don't have an underlying file are not supported. There was $zipFile"
+ if (zipFile.file == null) createWithoutUnderlyingFile(zipFile)
+ else ZipArchiveFlatClassPath(zipFile.file)
+
+ private def createWithoutUnderlyingFile(zipFile: AbstractFile) = zipFile match {
+ case manifestRes: ManifestResources =>
+ ManifestResourcesFlatClassPath(manifestRes)
+ case _ =>
+ val errorMsg = s"Abstract files which don't have an underlying file and are not ManifestResources are not supported. There was $zipFile"
throw new IllegalArgumentException(errorMsg)
- } else ZipArchiveFlatClassPath(zipFile.file)
+ }
}
/**