@@ -17,6 +17,8 @@ import scala.concurrent._
17
17
import java .nio .ByteBuffer
18
18
import java .nio .charset .StandardCharsets
19
19
20
+ import java .util .concurrent .atomic .AtomicInteger
21
+
20
22
import org .scalajs .logging .Logger
21
23
22
24
import org .scalajs .linker .interface .{IRFile , OutputDirectory , Report }
@@ -36,12 +38,19 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
36
38
37
39
import BasicLinkerBackend ._
38
40
41
+ private [this ] var totalModules = 0
42
+ private [this ] val rewrittenModules = new AtomicInteger (0 )
43
+
39
44
private [this ] val emitter = {
40
45
val emitterConfig = Emitter .Config (config.commonConfig.coreSpec)
41
46
.withJSHeader(config.jsHeader)
42
47
.withInternalModulePattern(m => OutputPatternsImpl .moduleName(config.outputPatterns, m.id))
43
48
44
- new Emitter (emitterConfig)
49
+ val postTransformer =
50
+ if (config.sourceMap) PostTransformerWithSourceMap
51
+ else PostTransformerWithoutSourceMap
52
+
53
+ new Emitter (emitterConfig, postTransformer)
45
54
}
46
55
47
56
val symbolRequirements : SymbolRequirement = emitter.symbolRequirements
@@ -61,31 +70,35 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
61
70
implicit ec : ExecutionContext ): Future [Report ] = {
62
71
verifyModuleSet(moduleSet)
63
72
73
+ // Reset stats.
74
+
75
+ totalModules = moduleSet.modules.size
76
+ rewrittenModules.set(0 )
77
+
64
78
val emitterResult = logger.time(" Emitter" ) {
65
79
emitter.emit(moduleSet, logger)
66
80
}
67
81
68
82
val skipContentCheck = ! isFirstRun
69
83
isFirstRun = false
70
84
71
- printedModuleSetCache.startRun(moduleSet)
72
85
val allChanged =
73
86
printedModuleSetCache.updateGlobal(emitterResult.header, emitterResult.footer)
74
87
75
88
val writer = new OutputWriter (output, config, skipContentCheck) {
76
89
protected def writeModuleWithoutSourceMap (moduleID : ModuleID , force : Boolean ): Option [ByteBuffer ] = {
77
90
val cache = printedModuleSetCache.getModuleCache(moduleID)
78
- val changed = cache.update( emitterResult.body(moduleID) )
91
+ val (printedTrees, changed) = emitterResult.body(moduleID)
79
92
80
93
if (force || changed || allChanged) {
81
- printedModuleSetCache.incRewrittenModules ()
94
+ rewrittenModules.incrementAndGet ()
82
95
83
96
val jsFileWriter = new ByteArrayWriter (sizeHintFor(cache.getPreviousFinalJSFileSize()))
84
97
85
98
jsFileWriter.write(printedModuleSetCache.headerBytes)
86
99
jsFileWriter.writeASCIIString(" 'use strict';\n " )
87
100
88
- for (printedTree <- cache. printedTrees)
101
+ for (printedTree <- printedTrees)
89
102
jsFileWriter.write(printedTree.jsCode)
90
103
91
104
jsFileWriter.write(printedModuleSetCache.footerBytes)
@@ -99,10 +112,10 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
99
112
100
113
protected def writeModuleWithSourceMap (moduleID : ModuleID , force : Boolean ): Option [(ByteBuffer , ByteBuffer )] = {
101
114
val cache = printedModuleSetCache.getModuleCache(moduleID)
102
- val changed = cache.update( emitterResult.body(moduleID) )
115
+ val (printedTrees, changed) = emitterResult.body(moduleID)
103
116
104
117
if (force || changed || allChanged) {
105
- printedModuleSetCache.incRewrittenModules ()
118
+ rewrittenModules.incrementAndGet ()
106
119
107
120
val jsFileWriter = new ByteArrayWriter (sizeHintFor(cache.getPreviousFinalJSFileSize()))
108
121
val sourceMapWriter = new ByteArrayWriter (sizeHintFor(cache.getPreviousFinalSourceMapSize()))
@@ -120,7 +133,7 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
120
133
jsFileWriter.writeASCIIString(" 'use strict';\n " )
121
134
smWriter.nextLine()
122
135
123
- for (printedTree <- cache. printedTrees) {
136
+ for (printedTree <- printedTrees) {
124
137
jsFileWriter.write(printedTree.jsCode)
125
138
smWriter.insertFragment(printedTree.sourceMapFragment)
126
139
}
@@ -145,9 +158,15 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
145
158
writer.write(moduleSet)
146
159
}.andThen { case _ =>
147
160
printedModuleSetCache.cleanAfterRun()
148
- printedModuleSetCache. logStats(logger)
161
+ logStats(logger)
149
162
}
150
163
}
164
+
165
+ private def logStats (logger : Logger ): Unit = {
166
+ // Message extracted in BasicLinkerBackendTest
167
+ logger.debug(
168
+ s " BasicBackend: total modules: $totalModules; re-written: ${rewrittenModules.get()}" )
169
+ }
151
170
}
152
171
153
172
private object BasicLinkerBackend {
@@ -161,20 +180,6 @@ private object BasicLinkerBackend {
161
180
162
181
private val modules = new java.util.concurrent.ConcurrentHashMap [ModuleID , PrintedModuleCache ]
163
182
164
- private var totalModules = 0
165
- private val rewrittenModules = new java.util.concurrent.atomic.AtomicInteger (0 )
166
-
167
- private var totalTopLevelTrees = 0
168
- private var recomputedTopLevelTrees = 0
169
-
170
- def startRun (moduleSet : ModuleSet ): Unit = {
171
- totalModules = moduleSet.modules.size
172
- rewrittenModules.set(0 )
173
-
174
- totalTopLevelTrees = 0
175
- recomputedTopLevelTrees = 0
176
- }
177
-
178
183
def updateGlobal (header : String , footer : String ): Boolean = {
179
184
if (header == lastHeader && footer == lastFooter) {
180
185
false
@@ -193,61 +198,30 @@ private object BasicLinkerBackend {
193
198
def headerNewLineCount : Int = _headerNewLineCountCache
194
199
195
200
def getModuleCache (moduleID : ModuleID ): PrintedModuleCache = {
196
- val result = modules.computeIfAbsent(moduleID, { _ =>
197
- if (withSourceMaps) new PrintedModuleCacheWithSourceMaps
198
- else new PrintedModuleCache
199
- })
200
-
201
+ val result = modules.computeIfAbsent(moduleID, _ => new PrintedModuleCache )
201
202
result.startRun()
202
203
result
203
204
}
204
205
205
- def incRewrittenModules (): Unit =
206
- rewrittenModules.incrementAndGet()
207
-
208
206
def cleanAfterRun (): Unit = {
209
207
val iter = modules.entrySet().iterator()
210
208
while (iter.hasNext()) {
211
209
val moduleCache = iter.next().getValue()
212
- if (moduleCache.cleanAfterRun()) {
213
- totalTopLevelTrees += moduleCache.getTotalTopLevelTrees
214
- recomputedTopLevelTrees += moduleCache.getRecomputedTopLevelTrees
215
- } else {
210
+ if (! moduleCache.cleanAfterRun()) {
216
211
iter.remove()
217
212
}
218
213
}
219
214
}
220
-
221
- def logStats (logger : Logger ): Unit = {
222
- /* These messages are extracted in BasicLinkerBackendTest to assert that
223
- * we do not invalidate anything in a no-op second run.
224
- */
225
- logger.debug(
226
- s " BasicBackend: total top-level trees: $totalTopLevelTrees; re-computed: $recomputedTopLevelTrees" )
227
- logger.debug(
228
- s " BasicBackend: total modules: $totalModules; re-written: ${rewrittenModules.get()}" )
229
- }
230
- }
231
-
232
- private final class PrintedTree (val jsCode : Array [Byte ], val sourceMapFragment : SourceMapWriter .Fragment ) {
233
- var cachedUsed : Boolean = false
234
215
}
235
216
236
217
private sealed class PrintedModuleCache {
237
218
private var cacheUsed = false
238
- private var changed = false
239
- private var lastJSTrees : List [js.Tree ] = Nil
240
- private var printedTreesCache : List [PrintedTree ] = Nil
241
- private val cache = new java.util.IdentityHashMap [js.Tree , PrintedTree ]
242
219
243
220
private var previousFinalJSFileSize : Int = 0
244
221
private var previousFinalSourceMapSize : Int = 0
245
222
246
- private var recomputedTopLevelTrees = 0
247
-
248
223
def startRun (): Unit = {
249
224
cacheUsed = true
250
- recomputedTopLevelTrees = 0
251
225
}
252
226
253
227
def getPreviousFinalJSFileSize (): Int = previousFinalJSFileSize
@@ -259,72 +233,42 @@ private object BasicLinkerBackend {
259
233
previousFinalSourceMapSize = finalSourceMapSize
260
234
}
261
235
262
- def update (newJSTrees : List [js.Tree ]): Boolean = {
263
- val changed = ! newJSTrees.corresponds(lastJSTrees)(_ eq _)
264
- this .changed = changed
265
- if (changed) {
266
- printedTreesCache = newJSTrees.map(getOrComputePrintedTree(_))
267
- lastJSTrees = newJSTrees
268
- }
269
- changed
270
- }
271
-
272
- private def getOrComputePrintedTree (tree : js.Tree ): PrintedTree = {
273
- val result = cache.computeIfAbsent(tree, { (tree : js.Tree ) =>
274
- recomputedTopLevelTrees += 1
275
- computePrintedTree(tree)
276
- })
277
-
278
- result.cachedUsed = true
279
- result
280
- }
281
-
282
- protected def computePrintedTree (tree : js.Tree ): PrintedTree = {
283
- val jsCodeWriter = new ByteArrayWriter ()
284
- val printer = new Printers .JSTreePrinter (jsCodeWriter)
285
-
286
- printer.printStat(tree)
287
-
288
- new PrintedTree (jsCodeWriter.toByteArray(), SourceMapWriter .Fragment .Empty )
236
+ def cleanAfterRun (): Boolean = {
237
+ val wasUsed = cacheUsed
238
+ cacheUsed = false
239
+ wasUsed
289
240
}
241
+ }
290
242
291
- def printedTrees : List [PrintedTree ] = printedTreesCache
243
+ private object PostTransformerWithoutSourceMap extends Emitter .PostTransformer [js.PrintedTree ] {
244
+ def transformStats (trees : List [js.Tree ], indent : Int ): List [js.PrintedTree ] = {
245
+ if (trees.isEmpty) {
246
+ Nil // Fast path
247
+ } else {
248
+ val jsCodeWriter = new ByteArrayWriter ()
249
+ val printer = new Printers .JSTreePrinter (jsCodeWriter, indent)
292
250
293
- def cleanAfterRun (): Boolean = {
294
- if (cacheUsed) {
295
- cacheUsed = false
296
-
297
- if (changed) {
298
- val iter = cache.entrySet().iterator()
299
- while (iter.hasNext()) {
300
- val printedTree = iter.next().getValue()
301
- if (printedTree.cachedUsed)
302
- printedTree.cachedUsed = false
303
- else
304
- iter.remove()
305
- }
306
- }
251
+ trees.map(printer.printStat(_))
307
252
308
- true
309
- } else {
310
- false
253
+ js.PrintedTree (jsCodeWriter.toByteArray(), SourceMapWriter .Fragment .Empty ) :: Nil
311
254
}
312
255
}
313
-
314
- def getTotalTopLevelTrees : Int = lastJSTrees.size
315
- def getRecomputedTopLevelTrees : Int = recomputedTopLevelTrees
316
256
}
317
257
318
- private final class PrintedModuleCacheWithSourceMaps extends PrintedModuleCache {
319
- override protected def computePrintedTree (tree : js.Tree ): PrintedTree = {
320
- val jsCodeWriter = new ByteArrayWriter ()
321
- val smFragmentBuilder = new SourceMapWriter .FragmentBuilder ()
322
- val printer = new Printers .JSTreePrinterWithSourceMap (jsCodeWriter, smFragmentBuilder)
258
+ private object PostTransformerWithSourceMap extends Emitter .PostTransformer [js.PrintedTree ] {
259
+ def transformStats (trees : List [js.Tree ], indent : Int ): List [js.PrintedTree ] = {
260
+ if (trees.isEmpty) {
261
+ Nil // Fast path
262
+ } else {
263
+ val jsCodeWriter = new ByteArrayWriter ()
264
+ val smFragmentBuilder = new SourceMapWriter .FragmentBuilder ()
265
+ val printer = new Printers .JSTreePrinterWithSourceMap (jsCodeWriter, smFragmentBuilder, indent)
323
266
324
- printer.printStat(tree )
325
- smFragmentBuilder.complete()
267
+ trees.map( printer.printStat(_) )
268
+ smFragmentBuilder.complete()
326
269
327
- new PrintedTree (jsCodeWriter.toByteArray(), smFragmentBuilder.result())
270
+ js.PrintedTree (jsCodeWriter.toByteArray(), smFragmentBuilder.result()) :: Nil
271
+ }
328
272
}
329
273
}
330
274
}
0 commit comments