Skip to content

DO NOT MERGE: On disk cache #4963

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
wip
  • Loading branch information
gzm0 committed Mar 16, 2024
commit 639d93b45097927ccf1db369c5534f3e49873477
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,15 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config)

protected def moduleChanged(moduleID: ModuleID): Boolean = true // no incremental support

protected def writeModuleWithoutSourceMap(moduleID: ModuleID): ByteBuffer = {
protected def writeModuleWithoutSourceMap(moduleID: ModuleID, prevFile: Option[ByteBuffer]): ByteBuffer = {
val jsFileWriter = new ByteArrayOutputStream()
val jsFileStrWriter = new java.io.OutputStreamWriter(jsFileWriter, StandardCharsets.UTF_8)
writeCode(jsFileStrWriter)
jsFileStrWriter.flush()
ByteBuffer.wrap(jsFileWriter.toByteArray())
}

protected def writeModuleWithSourceMap(moduleID: ModuleID): (ByteBuffer, ByteBuffer) = {
protected def writeModuleWithSourceMap(moduleID: ModuleID, prevFile: Option[ByteBuffer]): (ByteBuffer, ByteBuffer) = {
val jsFileURI = OutputPatternsImpl.jsFileURI(config.outputPatterns, moduleID.id)
val sourceMapURI = OutputPatternsImpl.sourceMapURI(config.outputPatterns, moduleID.id)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
protected def moduleChanged(moduleID: ModuleID): Boolean =
allChanged || emitterResult.body(moduleID)._2

protected def writeModuleWithoutSourceMap(moduleID: ModuleID): ByteBuffer = {
protected def writeModuleWithoutSourceMap(moduleID: ModuleID, prevFile: Option[ByteBuffer]): ByteBuffer = {
val cache = printedModuleSetCache.getModuleCache(moduleID)
val (trees, _) = emitterResult.body(moduleID)

Expand All @@ -104,7 +104,7 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
jsFileWriter.write(printedModuleSetCache.headerBytes)
jsFileWriter.writeASCIIString("'use strict';\n")

val printer = new Printers.JSTreePrinter(jsFileWriter)
val printer = new Printers.JSTreePrinter(prevFile, jsFileWriter)
for (tree <- trees)
printer.printStat(tree)

Expand All @@ -114,7 +114,7 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
jsFileWriter.toByteBuffer()
}

protected def writeModuleWithSourceMap(moduleID: ModuleID): (ByteBuffer, ByteBuffer) = {
protected def writeModuleWithSourceMap(moduleID: ModuleID, prevFile: Option[ByteBuffer]): (ByteBuffer, ByteBuffer) = {
val cache = printedModuleSetCache.getModuleCache(moduleID)
val (trees, _) = emitterResult.body(moduleID)

Expand All @@ -136,7 +136,7 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)
jsFileWriter.writeASCIIString("'use strict';\n")
smWriter.nextLine()

val printer = new Printers.JSTreePrinterWithSourceMap(jsFileWriter, smWriter, initIndent = 0)
val printer = new Printers.JSTreePrinterWithSourceMap(prevFile, jsFileWriter, smWriter, initIndent = 0)
for (tree <- trees)
printer.printStat(tree)

Expand Down Expand Up @@ -245,7 +245,7 @@ private object BasicLinkerBackend {
Nil // Fast path
} else {
val jsCodeWriter = new ByteArrayWriter()
val printer = new Printers.JSTreePrinter(jsCodeWriter, indent)
val printer = new Printers.JSTreePrinter(None, jsCodeWriter, indent)

trees.foreach(printer.printStat(_))

Expand All @@ -261,7 +261,7 @@ private object BasicLinkerBackend {
} else {
val jsCodeWriter = new ByteArrayWriter()
val smFragmentBuilder = new SourceMapWriter.FragmentBuilder()
val printer = new Printers.JSTreePrinterWithSourceMap(jsCodeWriter, smFragmentBuilder, indent)
val printer = new Printers.JSTreePrinterWithSourceMap(None, jsCodeWriter, smFragmentBuilder, indent)

trees.foreach(printer.printStat(_))
smFragmentBuilder.complete()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ private[backend] abstract class OutputWriter(output: OutputDirectory,

protected def moduleChanged(moduleID: ModuleID): Boolean

protected def writeModuleWithoutSourceMap(moduleID: ModuleID): ByteBuffer
protected def writeModuleWithoutSourceMap(moduleID: ModuleID, prevFile: Option[ByteBuffer]): ByteBuffer

protected def writeModuleWithSourceMap(moduleID: ModuleID): (ByteBuffer, ByteBuffer)
protected def writeModuleWithSourceMap(moduleID: ModuleID, prevFile: Option[ByteBuffer]): (ByteBuffer, ByteBuffer)

def write(moduleSet: ModuleSet)(implicit ec: ExecutionContext): Future[Report] = {
val ioThrottler = new IOThrottler(config.maxConcurrentWrites)
Expand Down Expand Up @@ -67,13 +67,20 @@ private[backend] abstract class OutputWriter(output: OutputDirectory,
implicit ec: ExecutionContext): Future[Report.Module] = {
val jsFileName = OutputPatternsImpl.jsFile(config.outputPatterns, moduleID.id)

val prevFileFuture =
if (existingFiles.contains(jsFileName)) outputImpl.readFull(jsFileName).map(Some(_))
else Future.successful(None)

if (config.sourceMap) {
val sourceMapFileName = OutputPatternsImpl.sourceMapFile(config.outputPatterns, moduleID.id)
val report = new ReportImpl.ModuleImpl(moduleID.id, jsFileName, Some(sourceMapFileName), moduleKind)

if (moduleChanged(moduleID)) {
val (code, sourceMap) = writeModuleWithSourceMap(moduleID)
for {
prevFile <- prevFileFuture
(code, sourceMap) = writeModuleWithSourceMap(moduleID, prevFile)

// TODO: We should not read the file again, but use the existing buffer to compare if a write is required.
_ <- outputImpl.writeFull(jsFileName, code, skipContentCheck)
_ <- outputImpl.writeFull(sourceMapFileName, sourceMap, skipContentCheck)
} yield {
Expand All @@ -86,8 +93,9 @@ private[backend] abstract class OutputWriter(output: OutputDirectory,
val report = new ReportImpl.ModuleImpl(moduleID.id, jsFileName, None, moduleKind)

if (moduleChanged(moduleID)) {
val code = writeModuleWithoutSourceMap(moduleID)
for {
prevFile <- prevFileFuture
code = writeModuleWithoutSourceMap(moduleID, prevFile)
_ <- outputImpl.writeFull(jsFileName, code, skipContentCheck)
} yield {
report
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ private[backend] final class ByteArrayWriter(originalCapacity: Int) extends Outp
size += 1
}

def write(bs: ByteBuffer): Unit = {
val len = bs.remaining()
val newSize = size + len
ensureCapacity(newSize)
bs.get(buffer, size, len)
size = newSize
}

override def write(bs: Array[Byte]): Unit =
write(bs, 0, bs.length)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

package org.scalajs.linker.backend.javascript

import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets

import scala.annotation.switch
Expand All @@ -33,7 +34,7 @@ import Trees._
object Printers {
private val ReusableIndentArray = Array.fill(128)(' '.toByte)

class JSTreePrinter(protected val out: ByteArrayWriter, initIndent: Int = 0) {
class JSTreePrinter(prevFile: Option[ByteBuffer], protected val out: ByteArrayWriter, initIndent: Int = 0) {
private final val IndentStep = 2

private var indentMargin = initIndent * IndentStep
Expand Down Expand Up @@ -762,8 +763,12 @@ object Printers {
print("]")
}

protected def print(printedTree: PrintedTree): Unit =
out.write(printedTree.jsCode)
protected def print(printedTree: PrintedTree): Unit = {
val startPos = out.currentSize
out.write(printedTree.jsCode(prevFile))
val endPos = out.currentSize
printedTree.written(startPos, endPos)
}

private def print(exportName: ExportName): Unit =
printEscapeJS(exportName.name)
Expand All @@ -776,9 +781,9 @@ object Printers {
out.write(c)
}

class JSTreePrinterWithSourceMap(_out: ByteArrayWriter,
class JSTreePrinterWithSourceMap(prevFile: Option[ByteBuffer], _out: ByteArrayWriter,
sourceMap: SourceMapWriter.Builder, initIndent: Int)
extends JSTreePrinter(_out, initIndent) {
extends JSTreePrinter(prevFile, _out, initIndent) {

private var column = 0

Expand Down Expand Up @@ -853,7 +858,7 @@ object Printers {

/** A printer that shows `Tree`s for debugging, not for pretty-printing. */
private class JSTreeShowPrinter(_out: ByteArrayWriter, initIndent: Int = 0)
extends JSTreePrinter(_out, initIndent) {
extends JSTreePrinter(None, _out, initIndent) {
def printTreeForShow(tree: Tree): Unit =
printTree(tree, isStat = true)

Expand All @@ -862,5 +867,9 @@ object Printers {
print(ident.resolver.debugString)
print(">")
}

override protected def print(printedTree: PrintedTree): Unit = {
print(printedTree.show)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
package org.scalajs.linker.backend.javascript

import scala.annotation.switch
import scala.concurrent.Future

import java.nio.charset.StandardCharsets
import java.nio.ByteBuffer

import org.scalajs.ir
import org.scalajs.ir.{OriginalName, Position}
Expand Down Expand Up @@ -548,14 +550,42 @@ object Trees {
* A cleaner abstraction would be to have something like ir.Tree.Transient
* (for different output formats), but for now, we do not need this.
*/
sealed case class PrintedTree(jsCode: Array[Byte],
sourceMapFragment: SourceMapWriter.Fragment) extends Tree {
sealed class PrintedTree private (
private[this] var state: PrintedTree.State,
val sourceMapFragment: SourceMapWriter.Fragment
) extends Tree {
val pos: Position = Position.NoPosition

override def show: String = new String(jsCode, StandardCharsets.UTF_8)
override def show: String = state match {
case PrintedTree.InMemory(jsCode) =>
new String(jsCode, StandardCharsets.UTF_8)

case PrintedTree.OnDisk(startPos, endPos) =>
f"<printed (on disk @ $startPos-$endPos)>"
}

def jsCode(prevFile: Option[ByteBuffer]): ByteBuffer = state match {
case PrintedTree.InMemory(jsCode) =>
ByteBuffer.wrap(jsCode).asReadOnlyBuffer()

case PrintedTree.OnDisk(startPos, endPos) =>
prevFile.get.slice().position(startPos).limit(endPos)
}

def written(startPos: Int, endPos: Int): Unit =
this.state = PrintedTree.OnDisk(startPos, endPos)
}

object PrintedTree {
def empty: PrintedTree = PrintedTree(Array(), SourceMapWriter.Fragment.Empty)

def apply(jsCode: Array[Byte],
sourceMapFragment: SourceMapWriter.Fragment): PrintedTree = {
new PrintedTree(InMemory(jsCode), sourceMapFragment)
}

sealed trait State
final case class InMemory(jsCode: Array[Byte]) extends State
final case class OnDisk(startPos: Int, endPos: Int) extends State
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class PrintersTest {

private def printTree(tree: Tree): String = {
val out = new ByteArrayWriter
val printer = new Printers.JSTreePrinter(out)
val printer = new Printers.JSTreePrinter(None, out)
printer.printStat(tree)
new String(out.toByteArray(), UTF_8)
}
Expand Down Expand Up @@ -224,6 +224,13 @@ class PrintersTest {
assertEquals("test", tree.show)
}

@Test def showWrittenPrintedTree(): Unit = {
val tree = PrintedTree("test".getBytes(UTF_8), SourceMapWriter.Fragment.Empty)
tree.written(100, 2000)

assertEquals("<printed (on disk @ 100-2000)>", tree.show)
}

@Test def showNestedPrintedTree(): Unit = {
val tree = PrintedTree(" test\n".getBytes(UTF_8), SourceMapWriter.Fragment.Empty)

Expand Down