@@ -5,38 +5,32 @@ package scala.tools.nsc.classpath
5
5
6
6
import java .io .File
7
7
import java .net .URL
8
+ import java .nio .file .Files
9
+ import java .nio .file .attribute .{BasicFileAttributes , FileTime }
8
10
9
11
import scala .annotation .tailrec
10
- import scala .reflect .io .{ AbstractFile , FileZipArchive , ManifestResources }
12
+ import scala .reflect .io .{AbstractFile , FileZipArchive , ManifestResources }
11
13
import scala .tools .nsc .util .{ClassPath , ClassRepresentation }
12
14
import scala .tools .nsc .Settings
13
15
import FileUtils ._
14
16
15
17
/**
16
18
* A trait providing an optional cache for classpath entries obtained from zip and jar files.
17
- * It's possible to create such a cache assuming that entries in such files won't change (at
18
- * least will be the same each time we'll load classpath during the lifetime of JVM process)
19
- * - unlike class and source files in directories, which can be modified and recompiled.
20
19
* It allows us to e.g. reduce significantly memory used by PresentationCompilers in Scala IDE
21
20
* when there are a lot of projects having a lot of common dependencies.
22
21
*/
23
22
sealed trait ZipAndJarFileLookupFactory {
24
- private val cache = collection.mutable. Map .empty[ AbstractFile , ClassPath ]
23
+ private val cache = new FileBasedCache [ ClassPath ]
25
24
26
25
def create (zipFile : AbstractFile , settings : Settings ): ClassPath = {
27
- if (settings.YdisableFlatCpCaching ) createForZipFile(zipFile)
26
+ if (settings.YdisableFlatCpCaching || zipFile.file == null ) createForZipFile(zipFile)
28
27
else createUsingCache(zipFile, settings)
29
28
}
30
29
31
30
protected def createForZipFile (zipFile : AbstractFile ): ClassPath
32
31
33
- private def createUsingCache (zipFile : AbstractFile , settings : Settings ): ClassPath = cache.synchronized {
34
- def newClassPathInstance = {
35
- if (settings.verbose || settings.Ylogcp )
36
- println(s " $zipFile is not yet in the classpath cache " )
37
- createForZipFile(zipFile)
38
- }
39
- cache.getOrElseUpdate(zipFile, newClassPathInstance)
32
+ private def createUsingCache (zipFile : AbstractFile , settings : Settings ): ClassPath = {
33
+ cache.getOrCreate(zipFile.file.toPath, () => createForZipFile(zipFile))
40
34
}
41
35
}
42
36
@@ -181,3 +175,29 @@ object ZipAndJarSourcePathFactory extends ZipAndJarFileLookupFactory {
181
175
182
176
override protected def createForZipFile (zipFile : AbstractFile ): ClassPath = ZipArchiveSourcePath (zipFile.file)
183
177
}
178
+
179
+ final class FileBasedCache [T ] {
180
+ private case class Stamp (lastModified : FileTime , fileKey : Object )
181
+ private val cache = collection.mutable.Map .empty[java.nio.file.Path , (Stamp , T )]
182
+
183
+ def getOrCreate (path : java.nio.file.Path , create : () => T ): T = cache.synchronized {
184
+ val attrs = Files .readAttributes(path, classOf [BasicFileAttributes ])
185
+ val lastModified = attrs.lastModifiedTime()
186
+ // only null on some platforms, but that's okay, we just use the last modified timestamp as our stamp
187
+ val fileKey = attrs.fileKey()
188
+ val stamp = Stamp (lastModified, fileKey)
189
+ cache.get(path) match {
190
+ case Some ((cachedStamp, cached)) if cachedStamp == stamp => cached
191
+ case _ =>
192
+ val value = create()
193
+ cache.put(path, (stamp, value))
194
+ value
195
+ }
196
+ }
197
+
198
+ def clear (): Unit = cache.synchronized {
199
+ // TODO support closing
200
+ // cache.valuesIterator.foreach(_.close())
201
+ cache.clear()
202
+ }
203
+ }
0 commit comments