Skip to content

Commit c1fd321

Browse files
lolgabsjrd
andauthored
Cache ScalaJS linkers for incremental linking (#1761)
* Cache Scala.js linkers for incremental linking * Use TrieMap instead of mutable.Map * Avoid caching fullLinkJS linker to save memory * Cache only one linker per target path * Use `withClosureCompiler` instead of `withClosureCompilerIfAvailable` * Avoid redundant `withOptimizer(true)` * Use SoftReference instead of WeakReference Co-authored-by: Sébastien Doeraene <sjrdoeraene@gmail.com>
1 parent 02877e3 commit c1fd321

File tree

1 file changed

+38
-20
lines changed

1 file changed

+38
-20
lines changed

bridges/scalajs-1/src/main/scala/bloop/scalajs/JsBridge.scala

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package bloop.scalajs
22

33
import java.nio.file.Path
44

5+
import scala.collection.concurrent.TrieMap
56
import scala.concurrent.Await
67
import scala.concurrent.ExecutionContext
78
import scala.concurrent.duration.Duration
9+
import scala.ref.SoftReference
810

911
import bloop.config.Config.JsConfig
1012
import bloop.config.Config.LinkerMode
@@ -51,6 +53,40 @@ object JsBridge {
5153
}
5254
override def trace(t: => Throwable): Unit = logger.trace(t)
5355
}
56+
private object ScalaJSLinker {
57+
private val cache = TrieMap.empty[Path, SoftReference[(JsConfig, Linker)]]
58+
def reuseOrCreate(config: JsConfig, target: Path): Linker =
59+
if (config.mode == LinkerMode.Release) createLinker(config)
60+
else
61+
cache.get(target) match {
62+
case Some(SoftReference((`config`, linker))) => linker
63+
case _ =>
64+
val newLinker = createLinker(config)
65+
cache.update(target, SoftReference((config, newLinker)))
66+
newLinker
67+
}
68+
private def createLinker(config: JsConfig): Linker = {
69+
val isFullLinkJS = config.mode == LinkerMode.Release
70+
val semantics =
71+
if (isFullLinkJS) Semantics.Defaults.optimized
72+
else Semantics.Defaults
73+
val scalaJSModuleKind = config.kind match {
74+
case ModuleKindJS.NoModule => ScalaJSModuleKind.NoModule
75+
case ModuleKindJS.CommonJSModule => ScalaJSModuleKind.CommonJSModule
76+
case ModuleKindJS.ESModule => ScalaJSModuleKind.ESModule
77+
}
78+
79+
val useClosure = isFullLinkJS && config.kind != ModuleKindJS.ESModule
80+
81+
val linkerConfig = StandardConfig()
82+
.withClosureCompiler(useClosure)
83+
.withSemantics(semantics)
84+
.withModuleKind(scalaJSModuleKind)
85+
.withSourceMap(config.emitSourceMaps)
86+
87+
StandardImpl.clearableLinker(linkerConfig)
88+
}
89+
}
5490

5591
def link(
5692
config: JsConfig,
@@ -64,17 +100,7 @@ object JsBridge {
64100
): Unit = {
65101
implicit val ec = executionContext
66102
implicit val logFilter: DebugFilter = DebugFilter.Link
67-
val enableOptimizer = config.mode == LinkerMode.Release
68-
val semantics = config.mode match {
69-
case LinkerMode.Debug => Semantics.Defaults
70-
case LinkerMode.Release => Semantics.Defaults.optimized
71-
}
72-
73-
val moduleKind = config.kind match {
74-
case ModuleKindJS.NoModule => ScalaJSModuleKind.NoModule
75-
case ModuleKindJS.CommonJSModule => ScalaJSModuleKind.CommonJSModule
76-
case ModuleKindJS.ESModule => ScalaJSModuleKind.ESModule
77-
}
103+
val linker = ScalaJSLinker.reuseOrCreate(config, target)
78104

79105
val cache = StandardImpl.irFileCache().newCache
80106
val irContainersPairs = PathIRContainer.fromClasspath(classpath)
@@ -101,18 +127,10 @@ object JsBridge {
101127
}
102128

103129
val output = LinkerOutput(PathOutputFile(target))
104-
val jsConfig = StandardConfig()
105-
.withOptimizer(enableOptimizer)
106-
.withClosureCompilerIfAvailable(enableOptimizer)
107-
.withSemantics(semantics)
108-
.withModuleKind(moduleKind)
109-
.withSourceMap(config.emitSourceMaps)
110130

111131
val resultFuture = for {
112132
libraryIRs <- libraryIrsFuture
113-
_ <- StandardImpl
114-
.linker(jsConfig)
115-
.link(libraryIRs, moduleInitializers, output, new Logger(logger))
133+
_ <- linker.link(libraryIRs, moduleInitializers, output, new Logger(logger))
116134
} yield ()
117135

118136
Await.result(resultFuture, Duration.Inf)

0 commit comments

Comments
 (0)