13
13
package scala .tools .nsc .classpath
14
14
15
15
import java .io .{Closeable , File }
16
- import java .net .URL
17
- import java .util
16
+ import java .net .{ URI , URL }
17
+ import java .nio . file . _
18
18
19
- import scala .reflect .io .{AbstractFile , PlainFile , PlainNioFile }
20
- import scala .tools .nsc .util .{ClassPath , ClassRepresentation , EfficientClassPath }
21
- import FileUtils ._
22
19
import scala .collection .JavaConverters ._
23
20
import scala .reflect .internal .JDK9Reflectors
21
+ import scala .reflect .io .{AbstractFile , PlainFile , PlainNioFile }
24
22
import scala .tools .nsc .CloseableRegistry
25
23
import 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 ._
26
28
27
29
/**
28
30
* 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
71
73
case None => emptyFiles
72
74
case Some (directory) => listChildren(directory, Some (isMatchingFile))
73
75
}
74
- files.map(f => createFileEntry(toAbstractFile(f)))
76
+ files.iterator. map(f => createFileEntry(toAbstractFile(f))).toSeq
75
77
}
76
78
77
79
override private [nsc] def list (inPackage : PackageName , onPackageEntry : PackageEntry => Unit , onClassesAndSources : ClassRepresentation => Unit ): Unit = {
@@ -95,7 +97,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
95
97
protected def emptyFiles : Array [File ] = Array .empty
96
98
protected def getSubDir (packageDirName : String ): Option [File ] = {
97
99
val packageDir = new File (dir, packageDirName)
98
- if (packageDir.exists && packageDir.isDirectory) Some (packageDir)
100
+ if (packageDir.exists && packageDir.isDirectory && packageDir.canRead ) Some (packageDir)
99
101
else None
100
102
}
101
103
protected def listChildren (dir : File , filter : Option [File => Boolean ]): Array [File ] = {
@@ -114,7 +116,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
114
116
//
115
117
// Note this behaviour can be enabled in javac with `javac -XDsortfiles`, but that's only
116
118
// 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))
118
120
listing
119
121
}
120
122
protected def getName (f : File ): String = f.getName
@@ -128,44 +130,65 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
128
130
}
129
131
130
132
object JrtClassPath {
131
- import java .nio .file ._ , java .net .URI
132
133
private val jrtClassPathCache = new FileBasedCache [Unit , JrtClassPath ]()
133
134
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
137
137
else {
138
138
// TODO escalate errors once we're sure they are fatal
139
139
// I'm hesitant to do this immediately, because -release will still work for multi-release JARs
140
140
// even if we're running on a JRE or a non OpenJDK JDK where ct.sym is unavailable.
141
141
//
142
142
// 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
144
144
145
145
val currentMajorVersion : Int = JDK9Reflectors .runtimeVersionMajor(JDK9Reflectors .runtimeVersion()).intValue()
146
146
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
157
156
}
158
157
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)
166
159
}
167
160
}
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
169
192
}
170
193
171
194
/**
@@ -176,16 +199,15 @@ object JrtClassPath {
176
199
*
177
200
* The implementation assumes that no classes exist in the empty package.
178
201
*/
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 {
181
203
type F = Path
182
204
private val dir : Path = fs.getPath(" /packages" )
183
205
184
206
// e.g. "java.lang" -> Seq("/modules/java.base")
185
207
private val packageToModuleBases : Map [String , Seq [Path ]] = {
186
- val ps = Files .newDirectoryStream(dir).iterator() .asScala
208
+ val ps = Files .newDirectoryStream(dir).iterator.asScala
187
209
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
189
211
}
190
212
ps.map(p => (p.toString.stripPrefix(" /packages/" ), lookup(p))).toMap
191
213
}
@@ -199,7 +221,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
199
221
if (inPackage.isRoot) Nil
200
222
else {
201
223
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 =>
203
225
ClassFileEntryImpl (new PlainNioFile (x))).toVector
204
226
}
205
227
}
@@ -208,7 +230,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
208
230
if (inPackage.isRoot) ClassPathEntries (packages(inPackage), Nil )
209
231
else ClassPathEntries (packages(inPackage), classes(inPackage))
210
232
211
- def asURLs : Seq [URL ] = Seq (new URL (" jrt:/" ))
233
+ def asURLs : Seq [URL ] = Seq (new URI (" jrt:/" ).toURL )
212
234
// We don't yet have a scheme to represent the JDK modules in our `-classpath`.
213
235
// java models them as entries in the new "module path", we'll probably need to follow this.
214
236
def asClassPathStrings : Seq [String ] = Nil
@@ -226,26 +248,26 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
226
248
}
227
249
228
250
/**
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
230
252
*/
231
253
final class CtSymClassPath (ctSym : java.nio.file.Path , release : Int ) extends ClassPath with NoSourcePaths with Closeable {
232
254
import java .nio .file .Path , java .nio .file ._
233
255
234
256
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
237
259
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
239
261
private def codeFor (major : Int ): String = if (major < 10 ) major.toString else ('A' + (major - 10 )).toChar.toString
240
262
241
263
private val releaseCode : String = codeFor(release)
242
264
private def fileNameMatchesRelease (fileName : String ) = ! fileName.contains(" -" ) && fileName.contains(releaseCode) // exclude `9-modules`
243
265
private val rootsForRelease : List [Path ] = roots.filter(root => fileNameMatchesRelease(root.getFileName.toString))
244
266
245
267
// 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 ]] = {
247
269
val index = collection.mutable.AnyRefMap [String , collection.mutable.ListBuffer [Path ]]()
248
- val isJava12OrHigher = scala.util. Properties . isJavaAtLeast(" 12" )
270
+ val isJava12OrHigher = isJavaAtLeast(" 12" )
249
271
rootsForRelease.foreach(root => Files .walk(root).iterator().asScala.filter(Files .isDirectory(_)).foreach { p =>
250
272
val moduleNamePathElementCount = if (isJava12OrHigher) 1 else 0
251
273
if (p.getNameCount > root.getNameCount + moduleNamePathElementCount) {
@@ -265,7 +287,7 @@ final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends Clas
265
287
if (inPackage.isRoot) Nil
266
288
else {
267
289
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" )))
269
291
sigFiles.map(f => ClassFileEntryImpl (new PlainNioFile (f))).toVector
270
292
}
271
293
}
0 commit comments