Skip to content

Commit 0bce3a8

Browse files
committed
Move the ClassPath cache to an explicit GlobalCache
1 parent 320e5ea commit 0bce3a8

File tree

6 files changed

+80
-35
lines changed

6 files changed

+80
-35
lines changed

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class Driver {
7070

7171
protected def command: CompilerCommand = ScalacCommand
7272

73+
protected def globalCache: GlobalCache = GlobalCache()
74+
7375
/** Setup context with initialized settings from CLI arguments, then check if there are any settings that
7476
* would change the default behaviour of the compiler.
7577
*
@@ -81,6 +83,7 @@ class Driver {
8183
val ictx = rootCtx.fresh
8284
val summary = command.distill(args, ictx.settings)(ictx.settingsState)(using ictx)
8385
ictx.setSettings(summary.sstate)
86+
ictx.setGlobalCache(globalCache)
8487
MacroClassLoader.init(ictx)
8588
Positioned.init(using ictx)
8689

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package dotty.tools.dotc
2+
3+
import java.nio.file.Files
4+
import java.nio.file.attribute.BasicFileAttributes
5+
import java.nio.file.attribute.FileTime
6+
7+
import dotty.tools.dotc.core.Contexts.Context
8+
import dotty.tools.io.{AbstractFile, ClassPath}
9+
10+
/** Global cache that can be shared across [[Driver]] instances.
11+
*
12+
* This class is thread-safe.
13+
*/
14+
final class GlobalCache():
15+
import GlobalCache.FileBasedCache
16+
17+
private val classPathCache = FileBasedCache[ClassPath]()
18+
19+
def getOrCreateClassPath(key: AbstractFile, createValue: => ClassPath)(using Context): ClassPath =
20+
classPathCache.getOrCreate(key.file.nn.toPath, () => createValue)
21+
22+
object GlobalCache:
23+
24+
/** A cache for values associated with files on disk, that invalidates
25+
* the cached value when the file is modified.
26+
*
27+
* See https://github.com/scala/bug/issues/10295 for some context on the
28+
* invalidation strategy.
29+
*
30+
* Moved from [[ZipAndJarFileLookupFactory]] in December 2025.
31+
*
32+
* @author @allanrenucci
33+
*/
34+
private class FileBasedCache[T]:
35+
private case class Stamp(lastModified: FileTime, fileKey: Object)
36+
private val cache = collection.mutable.Map.empty[java.nio.file.Path, (Stamp, T)]
37+
38+
def getOrCreate(path: java.nio.file.Path, create: () => T): T =
39+
cache.synchronized:
40+
val attrs = Files.readAttributes(path, classOf[BasicFileAttributes])
41+
val lastModified = attrs.lastModifiedTime()
42+
// null on some platforms, but that's okay, we just use the last
43+
// modified timestamp as our stamp in that case
44+
val fileKey = attrs.fileKey()
45+
val stamp = Stamp(lastModified, fileKey)
46+
cache.get(path) match
47+
case Some((cachedStamp, cached)) if cachedStamp == stamp =>
48+
cached
49+
case _ =>
50+
val value = create()
51+
cache.put(path, (stamp, value))
52+
value

compiler/src/dotty/tools/dotc/classpath/ZipAndJarFileLookupFactory.scala

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,12 @@ import FileUtils.*
2222
* when there are a lot of projects having a lot of common dependencies.
2323
*/
2424
sealed trait ZipAndJarFileLookupFactory {
25-
private val cache = new FileBasedCache[ClassPath]
26-
2725
def create(zipFile: AbstractFile)(using Context): ClassPath =
2826
val release = Option(ctx.settings.javaOutputVersion.value).filter(_.nonEmpty)
2927
if (ctx.settings.YdisableFlatCpCaching.value || zipFile.file == null) createForZipFile(zipFile, release)
30-
else createUsingCache(zipFile, release)
28+
else ctx.globalCache.getOrCreateClassPath(zipFile, createForZipFile(zipFile, release))
3129

3230
protected def createForZipFile(zipFile: AbstractFile, release: Option[String]): ClassPath
33-
34-
private def createUsingCache(zipFile: AbstractFile, release: Option[String]): ClassPath =
35-
cache.getOrCreate(zipFile.file.toPath, () => createForZipFile(zipFile, release))
3631
}
3732

3833
/**
@@ -172,29 +167,3 @@ object ZipAndJarSourcePathFactory extends ZipAndJarFileLookupFactory {
172167

173168
override protected def createForZipFile(zipFile: AbstractFile, release: Option[String]): ClassPath = ZipArchiveSourcePath(zipFile.file)
174169
}
175-
176-
final class FileBasedCache[T] {
177-
private case class Stamp(lastModified: FileTime, fileKey: Object)
178-
private val cache = collection.mutable.Map.empty[java.nio.file.Path, (Stamp, T)]
179-
180-
def getOrCreate(path: java.nio.file.Path, create: () => T): T = cache.synchronized {
181-
val attrs = Files.readAttributes(path, classOf[BasicFileAttributes])
182-
val lastModified = attrs.lastModifiedTime()
183-
// only null on some platforms, but that's okay, we just use the last modified timestamp as our stamp
184-
val fileKey = attrs.fileKey()
185-
val stamp = Stamp(lastModified, fileKey)
186-
cache.get(path) match {
187-
case Some((cachedStamp, cached)) if cachedStamp == stamp => cached
188-
case _ =>
189-
val value = create()
190-
cache.put(path, (stamp, value))
191-
value
192-
}
193-
}
194-
195-
def clear(): Unit = cache.synchronized {
196-
// TODO support closing
197-
// cache.valuesIterator.foreach(_.close())
198-
cache.clear()
199-
}
200-
}

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ object Contexts {
5757
private val (importInfoLoc, store9) = store8.newLocation[ImportInfo | Null]()
5858
private val (typeAssignerLoc, store10) = store9.newLocation[TypeAssigner](TypeAssigner)
5959
private val (progressCallbackLoc, store11) = store10.newLocation[ProgressCallback | Null]()
60+
private val (globalCacheLoc, store12) = store11.newLocation[GlobalCache]()
6061

61-
private val initialStore = store11
62+
private val initialStore = store12
6263

6364
/** The current context */
6465
inline def ctx(using ctx: Context): Context = ctx
@@ -189,6 +190,8 @@ object Contexts {
189190
val local = progressCallback
190191
if local != null then op(local)
191192

193+
def globalCache: GlobalCache = store(globalCacheLoc)
194+
192195
/** The current plain printer */
193196
def printerFn: Context => Printer = store(printerFnLoc)
194197

@@ -712,6 +715,7 @@ object Contexts {
712715
def setCompilerCallback(callback: CompilerCallback): this.type = updateStore(compilerCallbackLoc, callback)
713716
def setIncCallback(callback: IncrementalCallback): this.type = updateStore(incCallbackLoc, callback)
714717
def setProgressCallback(callback: ProgressCallback): this.type = updateStore(progressCallbackLoc, callback)
718+
def setGlobalCache(globalCache: GlobalCache): this.type = updateStore(globalCacheLoc, globalCache)
715719
def setPrinterFn(printer: Context => Printer): this.type = updateStore(printerFnLoc, printer)
716720
def setSettings(settingsState: SettingsState): this.type = updateStore(settingsStateLoc, settingsState)
717721
def setRun(run: Run | Null): this.type = updateStore(runLoc, run)

sbt-bridge/src/dotty/tools/xsbt/CompilerBridge.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package dotty.tools.xsbt;
77

8+
import dotty.tools.dotc.GlobalCache;
9+
810
import xsbti.AnalysisCallback;
911
import xsbti.Logger;
1012
import xsbti.Reporter;
@@ -15,10 +17,12 @@
1517
import xsbti.compile.Output;
1618

1719
public final class CompilerBridge implements CompilerInterface2 {
20+
private final GlobalCache globalCache = new GlobalCache();
21+
1822
@Override
1923
public void run(VirtualFile[] sources, DependencyChanges changes, String[] options, Output output,
2024
AnalysisCallback callback, Reporter delegate, CompileProgress progress, Logger log) {
21-
CompilerBridgeDriver driver = new CompilerBridgeDriver(options, output);
25+
CompilerBridgeDriver driver = new CompilerBridgeDriver(options, output, globalCache);
2226
driver.run(sources, callback, log, delegate, progress);
2327
}
2428
}

sbt-bridge/src/dotty/tools/xsbt/CompilerBridgeDriver.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import dotty.tools.dotc.Compiler;
99
import dotty.tools.dotc.Driver;
10+
import dotty.tools.dotc.GlobalCache;
1011
import dotty.tools.dotc.ScalacCommand;
1112
import dotty.tools.dotc.config.Properties;
1213
import dotty.tools.dotc.core.Contexts;
@@ -35,8 +36,9 @@
3536
public class CompilerBridgeDriver extends Driver {
3637
private final String[] scalacOptions;
3738
private final String[] args;
39+
private final GlobalCache globalCache;
3840

39-
public CompilerBridgeDriver(String[] scalacOptions, Output output) {
41+
public CompilerBridgeDriver(String[] scalacOptions, Output output, GlobalCache globalCache) {
4042
super();
4143
this.scalacOptions = scalacOptions;
4244

@@ -47,6 +49,12 @@ public CompilerBridgeDriver(String[] scalacOptions, Output output) {
4749
System.arraycopy(scalacOptions, 0, args, 0, scalacOptions.length);
4850
args[scalacOptions.length] = "-d";
4951
args[scalacOptions.length + 1] = output.getSingleOutputAsPath().get().toAbsolutePath().toString();
52+
53+
this.globalCache = globalCache;
54+
}
55+
56+
public CompilerBridgeDriver(String[] scalacOptions, Output output) {
57+
this(scalacOptions, output, new GlobalCache());
5058
}
5159

5260
private static final String StopInfoError =
@@ -61,6 +69,11 @@ public boolean sourcesRequired() {
6169
return false;
6270
}
6371

72+
@Override
73+
public GlobalCache globalCache() {
74+
return this.globalCache;
75+
}
76+
6477
private static VirtualFile asVirtualFile(SourceFile sourceFile, DelegatingReporter reporter,
6578
HashMap<AbstractFile, VirtualFile> lookup) {
6679
return lookup.computeIfAbsent(sourceFile.file(), path -> {

0 commit comments

Comments
 (0)