1313package scala .tools .nsc .classpath
1414
1515import java .io .{Closeable , File }
16- import java .net .URL
17- import java .util
16+ import java .net .{ URI , URL }
17+ import java .nio . file . _
1818
19- import scala .reflect .io .{AbstractFile , PlainFile , PlainNioFile }
20- import scala .tools .nsc .util .{ClassPath , ClassRepresentation , EfficientClassPath }
21- import FileUtils ._
2219import scala .collection .JavaConverters ._
2320import scala .reflect .internal .JDK9Reflectors
21+ import scala .reflect .io .{AbstractFile , PlainFile , PlainNioFile }
2422import scala .tools .nsc .CloseableRegistry
2523import scala .tools .nsc .classpath .PackageNameUtils .{packageContains , separatePkgAndClassNames }
24+ import scala .tools .nsc .util .{ClassPath , ClassRepresentation , EfficientClassPath }
25+ import scala .util .Properties .{isJavaAtLeast , javaHome }
26+ import scala .util .control .NonFatal
27+ import FileUtils ._
2628
2729/**
2830 * A trait allowing to look for classpath entries in directories. It provides common logic for
@@ -71,7 +73,7 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends EfficientCla
7173 case None => emptyFiles
7274 case Some (directory) => listChildren(directory, Some (isMatchingFile))
7375 }
74- files.map(f => createFileEntry(toAbstractFile(f)))
76+ files.iterator. map(f => createFileEntry(toAbstractFile(f))).toSeq
7577 }
7678
7779 override private [nsc] def list (inPackage : PackageName , onPackageEntry : PackageEntry => Unit , onClassesAndSources : ClassRepresentation => Unit ): Unit = {
@@ -95,7 +97,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
9597 protected def emptyFiles : Array [File ] = Array .empty
9698 protected def getSubDir (packageDirName : String ): Option [File ] = {
9799 val packageDir = new File (dir, packageDirName)
98- if (packageDir.exists && packageDir.isDirectory) Some (packageDir)
100+ if (packageDir.exists && packageDir.isDirectory && packageDir.canRead ) Some (packageDir)
99101 else None
100102 }
101103 protected def listChildren (dir : File , filter : Option [File => Boolean ]): Array [File ] = {
@@ -114,7 +116,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
114116 //
115117 // Note this behaviour can be enabled in javac with `javac -XDsortfiles`, but that's only
116118 // intended to improve determinism of the compiler for compiler hackers.
117- util.Arrays .sort(listing, (o1 : File , o2 : File ) => o1.getName.compareTo(o2.getName))
119+ java. util.Arrays .sort(listing, (o1 : File , o2 : File ) => o1.getName.compareTo(o2.getName))
118120 listing
119121 }
120122 protected def getName (f : File ): String = f.getName
@@ -128,44 +130,65 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
128130}
129131
130132object JrtClassPath {
131- import java .nio .file ._ , java .net .URI
132133 private val jrtClassPathCache = new FileBasedCache [Unit , JrtClassPath ]()
133134 private val ctSymClassPathCache = new FileBasedCache [String , CtSymClassPath ]()
134- def apply (release : Option [String ], closeableRegistry : CloseableRegistry ): Option [ClassPath ] = {
135- import scala .util .Properties ._
136- if (! isJavaAtLeast(" 9" )) None
135+ def apply (release : Option [String ], unsafe : Option [List [String ]], closeableRegistry : CloseableRegistry ): List [ClassPath ] =
136+ if (! isJavaAtLeast(" 9" )) Nil
137137 else {
138138 // TODO escalate errors once we're sure they are fatal
139139 // I'm hesitant to do this immediately, because -release will still work for multi-release JARs
140140 // even if we're running on a JRE or a non OpenJDK JDK where ct.sym is unavailable.
141141 //
142142 // Longer term we'd like an official API for this in the JDK
143- // Discussion: http ://mail.openjdk.java.net/pipermail/compiler-dev/2018-March/thread.html#11738
143+ // Discussion: https ://mail.openjdk.java.net/pipermail/compiler-dev/2018-March/thread.html#11738
144144
145145 val currentMajorVersion : Int = JDK9Reflectors .runtimeVersionMajor(JDK9Reflectors .runtimeVersion()).intValue()
146146 release match {
147- case Some (v) if v.toInt < currentMajorVersion =>
148- try {
149- val ctSym = Paths .get(javaHome).resolve(" lib" ).resolve(" ct.sym" )
150- if (Files .notExists(ctSym)) None
151- else {
152- val classPath = ctSymClassPathCache.getOrCreate(v, ctSym :: Nil , () => new CtSymClassPath (ctSym, v.toInt), closeableRegistry, true )
153- Some (classPath)
154- }
155- } catch {
156- case _ : Throwable => None
147+ case Some (version) if version.toInt < currentMajorVersion =>
148+ val ct = createCt(version, closeableRegistry)
149+ unsafe match {
150+ case Some (pkgs) if pkgs.nonEmpty =>
151+ createJrt(closeableRegistry) match {
152+ case Nil => ct
153+ case jrt :: _ => ct :+ new FilteringJrtClassPath (jrt, pkgs : _* )
154+ }
155+ case _ => ct
157156 }
158157 case _ =>
159- try {
160- val fs = FileSystems .getFileSystem(URI .create(" jrt:/" ))
161- val classPath = jrtClassPathCache.getOrCreate((), Nil , () => new JrtClassPath (fs), closeableRegistry, false )
162- Some (classPath)
163- } catch {
164- case _ : ProviderNotFoundException | _ : FileSystemNotFoundException => None
165- }
158+ createJrt(closeableRegistry)
166159 }
167160 }
168- }
161+ private def createCt (v : String , closeableRegistry : CloseableRegistry ): List [ClassPath ] =
162+ try {
163+ val ctSym = Paths .get(javaHome).resolve(" lib" ).resolve(" ct.sym" )
164+ if (Files .notExists(ctSym)) Nil
165+ else List (
166+ ctSymClassPathCache.getOrCreate(v, ctSym :: Nil , () => new CtSymClassPath (ctSym, v.toInt), closeableRegistry, checkStamps = true )
167+ )
168+ } catch {
169+ case NonFatal (_) => Nil
170+ }
171+ private def createJrt (closeableRegistry : CloseableRegistry ): List [JrtClassPath ] =
172+ try {
173+ val fs = FileSystems .getFileSystem(URI .create(" jrt:/" ))
174+ val classPath = jrtClassPathCache.getOrCreate((), Nil , () => new JrtClassPath (fs), closeableRegistry, checkStamps = false )
175+ List (classPath)
176+ } catch {
177+ case _ : ProviderNotFoundException | _ : FileSystemNotFoundException => Nil
178+ }
179+ }
180+
181+ final class FilteringJrtClassPath (delegate : JrtClassPath , allowed : String * ) extends ClassPath with NoSourcePaths {
182+ private val allowedPackages = allowed
183+ private def packagePrefix (p : String , q : String ) = p.startsWith(q) && (p.length == q.length || p.charAt(q.length) == '.' )
184+ private def ok (pkg : PackageName ) = pkg.dottedString.isEmpty || allowedPackages.exists(packagePrefix(_, pkg.dottedString))
185+ def asClassPathStrings : Seq [String ] = delegate.asClassPathStrings
186+ def asURLs : Seq [java.net.URL ] = delegate.asURLs
187+ private [nsc] def classes (inPackage : PackageName ) = if (ok(inPackage)) delegate.classes(inPackage) else Nil
188+ def findClassFile (className : String ) = if (ok(PackageName (separatePkgAndClassNames(className)._1))) delegate.findClassFile(className) else None
189+ private [nsc] def hasPackage (pkg : PackageName ) = ok(pkg) && delegate.hasPackage(pkg)
190+ private [nsc] def list (inPackage : PackageName ) = if (ok(inPackage)) delegate.list(inPackage) else ClassPathEntries (Nil , Nil )
191+ private [nsc] def packages (inPackage : PackageName ) = if (ok(inPackage)) delegate.packages(inPackage) else Nil
169192}
170193
171194/**
@@ -176,16 +199,15 @@ object JrtClassPath {
176199 *
177200 * The implementation assumes that no classes exist in the empty package.
178201 */
179- final class JrtClassPath (fs : java.nio.file.FileSystem ) extends ClassPath with NoSourcePaths {
180- import java .nio .file .Path , java .nio .file ._
202+ final class JrtClassPath (fs : FileSystem ) extends ClassPath with NoSourcePaths {
181203 type F = Path
182204 private val dir : Path = fs.getPath(" /packages" )
183205
184206 // e.g. "java.lang" -> Seq("/modules/java.base")
185207 private val packageToModuleBases : Map [String , Seq [Path ]] = {
186- val ps = Files .newDirectoryStream(dir).iterator() .asScala
208+ val ps = Files .newDirectoryStream(dir).iterator.asScala
187209 def lookup (pack : Path ): Seq [Path ] = {
188- Files .list(pack).iterator() .asScala.map(l => if (Files .isSymbolicLink(l)) Files .readSymbolicLink(l) else l).toList
210+ Files .list(pack).iterator.asScala.map(l => if (Files .isSymbolicLink(l)) Files .readSymbolicLink(l) else l).toList
189211 }
190212 ps.map(p => (p.toString.stripPrefix(" /packages/" ), lookup(p))).toMap
191213 }
@@ -199,7 +221,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
199221 if (inPackage.isRoot) Nil
200222 else {
201223 packageToModuleBases.getOrElse(inPackage.dottedString, Nil ).flatMap(x =>
202- Files .list(x.resolve(inPackage.dirPathTrailingSlash)).iterator() .asScala.filter(_.getFileName.toString.endsWith(" .class" ))).map(x =>
224+ Files .list(x.resolve(inPackage.dirPathTrailingSlash)).iterator.asScala.filter(_.getFileName.toString.endsWith(" .class" ))).map(x =>
203225 ClassFileEntryImpl (new PlainNioFile (x))).toVector
204226 }
205227 }
@@ -208,7 +230,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
208230 if (inPackage.isRoot) ClassPathEntries (packages(inPackage), Nil )
209231 else ClassPathEntries (packages(inPackage), classes(inPackage))
210232
211- def asURLs : Seq [URL ] = Seq (new URL (" jrt:/" ))
233+ def asURLs : Seq [URL ] = Seq (new URI (" jrt:/" ).toURL )
212234 // We don't yet have a scheme to represent the JDK modules in our `-classpath`.
213235 // java models them as entries in the new "module path", we'll probably need to follow this.
214236 def asClassPathStrings : Seq [String ] = Nil
@@ -226,26 +248,26 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
226248}
227249
228250/**
229- * Implementation `ClassPath` based on the \$JAVA_HOME/lib/ct.sym backing http ://openjdk.java.net/jeps/247
251+ * Implementation `ClassPath` based on the \$JAVA_HOME/lib/ct.sym backing https ://openjdk.java.net/jeps/247
230252 */
231253final class CtSymClassPath (ctSym : java.nio.file.Path , release : Int ) extends ClassPath with NoSourcePaths with Closeable {
232254 import java .nio .file .Path , java .nio .file ._
233255
234256 private val fileSystem : FileSystem = FileSystems .newFileSystem(ctSym, null : ClassLoader )
235- private val root : Path = fileSystem.getRootDirectories.iterator() .next
236- private val roots = Files .newDirectoryStream(root).iterator() .asScala.toList
257+ private val root : Path = fileSystem.getRootDirectories.iterator.next
258+ private val roots = Files .newDirectoryStream(root).iterator.asScala.toList
237259
238- // http ://mail.openjdk.java.net/pipermail/compiler-dev/2018-March/011737.html
260+ // https ://mail.openjdk.java.net/pipermail/compiler-dev/2018-March/011737.html
239261 private def codeFor (major : Int ): String = if (major < 10 ) major.toString else ('A' + (major - 10 )).toChar.toString
240262
241263 private val releaseCode : String = codeFor(release)
242264 private def fileNameMatchesRelease (fileName : String ) = ! fileName.contains(" -" ) && fileName.contains(releaseCode) // exclude `9-modules`
243265 private val rootsForRelease : List [Path ] = roots.filter(root => fileNameMatchesRelease(root.getFileName.toString))
244266
245267 // e.g. "java.lang" -> Seq(/876/java/lang, /87/java/lang, /8/java/lang))
246- private val packageIndex : scala.collection.Map [String , Seq [Path ]] = {
268+ private val packageIndex : scala.collection.Map [String , scala.collection. Seq [Path ]] = {
247269 val index = collection.mutable.AnyRefMap [String , collection.mutable.ListBuffer [Path ]]()
248- val isJava12OrHigher = scala.util. Properties . isJavaAtLeast(" 12" )
270+ val isJava12OrHigher = isJavaAtLeast(" 12" )
249271 rootsForRelease.foreach(root => Files .walk(root).iterator().asScala.filter(Files .isDirectory(_)).foreach { p =>
250272 val moduleNamePathElementCount = if (isJava12OrHigher) 1 else 0
251273 if (p.getNameCount > root.getNameCount + moduleNamePathElementCount) {
@@ -265,7 +287,7 @@ final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends Clas
265287 if (inPackage.isRoot) Nil
266288 else {
267289 val sigFiles = packageIndex.getOrElse(inPackage.dottedString, Nil ).iterator.flatMap(p =>
268- Files .list(p).iterator() .asScala.filter(_.getFileName.toString.endsWith(" .sig" )))
290+ Files .list(p).iterator.asScala.filter(_.getFileName.toString.endsWith(" .sig" )))
269291 sigFiles.map(f => ClassFileEntryImpl (new PlainNioFile (f))).toVector
270292 }
271293 }
0 commit comments