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