Skip to content

Commit

Permalink
Use JarWriter only when target jar is empty
Browse files Browse the repository at this point in the history
  • Loading branch information
WojciechMazur committed May 31, 2022
1 parent d5c7933 commit fc9b098
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 61 deletions.
86 changes: 45 additions & 41 deletions compiler/src/dotty/tools/backend/jvm/ClassfileWriter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,34 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
private val dumpOutputDir: AbstractFile = getDirectoryOrNull(compilerSettings.dumpClassesDirectory)

// if non-null, classfiles are written to a jar instead of the output directory
private val jarWriter: JarWriter =
val f = compilerSettings.outputDirectory
if f.hasExtension("jar") then
// If no main class was specified, see if there's only one
// entry point among the classes going into the jar.
val mainClass = compilerSettings.mainClass match {
case c @ Some(m) =>
backendReporting.log(s"Main-Class was specified: ${m}")
c
case None => frontendAccess.getEntryPoints match {
case Nil =>
backendReporting.log("No Main-Class designated or discovered.")
None
private val jarWriter: JarWriter | Null = compilerSettings.outputDirectory match {
case jar: JarArchive =>
val mainClass = compilerSettings.mainClass.orElse {
// If no main class was specified, see if there's only one
// entry point among the classes going into the jar.
frontendAccess.getEntryPoints match {
case name :: Nil =>
backendReporting.log(s"Unique entry point: setting Main-Class to $name")
Some(name)
case names =>
backendReporting.log(s"No Main-Class due to multiple entry points:\n ${names.mkString("\n ")}")
if names.isEmpty then backendReporting.warning("No Main-Class designated or discovered.")
else backendReporting.warning(s"No Main-Class due to multiple entry points:\n ${names.mkString("\n ")}")
None
}
}

val jarMainAttrs = mainClass.map(c => Name.MAIN_CLASS -> c).toList
new Jar(f.file).jarWriter(jarMainAttrs: _*)
else null
jar.underlyingSource.map{ source =>
if jar.isEmpty then
val jarMainAttrs = mainClass.map(Name.MAIN_CLASS -> _).toList
new Jar(source.file).jarWriter(jarMainAttrs: _*)
else
// Writing to non-empty JAR might be an undefined behaviour, e.g. in case if other files where
// created using `AbstractFile.bufferedOutputStream`instead of JarWritter
backendReporting.warning(s"Tried to write to non-empty JAR: $source")
null
}.orNull

case _ => null
}

private def getDirectoryOrNull(dir: Option[String]): AbstractFile =
dir.map(d => new PlainDirectory(Directory(d))).orNull
Expand Down Expand Up @@ -88,20 +91,9 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
}
}

def write(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): AbstractFile | Null = try {
def writeClass(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): AbstractFile | Null = try {
// val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer)
val outFile = if (jarWriter == null) {
val outFolder = compilerSettings.outputDirectory
val outFile = getFile(outFolder, className, ".class")
writeBytes(outFile, bytes)
outFile
} else {
val path = className + ".class"
val out = jarWriter.newOutputStream(path)
try out.write(bytes, 0, bytes.length)
finally out.flush()
null
}
val outFile = writeToJarOrFile(className, bytes, ".class")
// Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart)

if (dumpOutputDir != null) {
Expand All @@ -111,22 +103,34 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
outFile
} catch {
case e: FileConflictException =>
backendReporting.error(s"error writing $className: ${e.getMessage}", NoSourcePosition)
backendReporting.error(s"error writing $className: ${e.getMessage}")
null
case e: java.nio.file.FileSystemException =>
if compilerSettings.debug then e.printStackTrace()
backendReporting.error(s"error writing $className: ${e.getClass.getName} ${e.getMessage}", NoSourcePosition)
backendReporting.error(s"error writing $className: ${e.getClass.getName} ${e.getMessage}")
null
}

def writeTasty(className: InternalName, bytes: Array[Byte]): Unit =
val outFolder = compilerSettings.outputDirectory
val outFile = getFile(outFolder, className, ".tasty")
try writeBytes(outFile, bytes)
catch case ex: ClosedByInterruptException =>
try outFile.delete() // don't leave an empty or half-written tastyfile around after an interrupt
catch case _: Throwable => ()
finally throw ex
def writeTasty(className: InternalName, bytes: Array[Byte]): Unit =
writeToJarOrFile(className, bytes, ".tasty")

private def writeToJarOrFile(className: InternalName, bytes: Array[Byte], suffix: String): AbstractFile | Null = {
if jarWriter == null then
val outFolder = compilerSettings.outputDirectory
val outFile = getFile(outFolder, className, suffix)
try writeBytes(outFile, bytes)
catch case ex: ClosedByInterruptException =>
try outFile.delete() // don't leave an empty or half-written files around after an interrupt
catch case _: Throwable => ()
finally throw ex
outFile
else
val path = className + suffix
val out = jarWriter.newOutputStream(path)
try out.write(bytes, 0, bytes.length)
finally out.flush()
null
}

def close(): Unit = {
if (jarWriter != null) jarWriter.close()
Expand Down
7 changes: 3 additions & 4 deletions compiler/src/dotty/tools/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ class GenBCode extends Phase { self =>
superCallsMap.update(sym, old + calls)
}

private[jvm] val entryPoints = new mutable.HashSet[String]()
def registerEntryPoint(s: String): Unit =
entryPoints += s
private val entryPoints = new mutable.HashSet[String]()
def registerEntryPoint(s: String): Unit = entryPoints += s

private var _backendInterface: DottyBackendInterface = _
def backendInterface(using Context): DottyBackendInterface = {
Expand Down Expand Up @@ -52,7 +51,7 @@ class GenBCode extends Phase { self =>
private var _frontendAccess: PostProcessorFrontendAccess = _
def frontendAccess(using Context): PostProcessorFrontendAccess = {
if _frontendAccess eq null then
_frontendAccess = PostProcessorFrontendAccess.Impl(backendInterface)
_frontendAccess = PostProcessorFrontendAccess.Impl(backendInterface, entryPoints)
_frontendAccess
}

Expand Down
11 changes: 4 additions & 7 deletions compiler/src/dotty/tools/backend/jvm/PostProcessor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes:
import int.given

val backendUtils = new BackendUtils(this)
lazy val classfileWriter = ClassfileWriter(frontendAccess)
val classfileWriter = ClassfileWriter(frontendAccess)

def postProcessAndSendToDisk(generatedDefs: GeneratedDefs): Unit = {
val GeneratedDefs(classes, tasty) = generatedDefs
Expand All @@ -30,21 +30,18 @@ class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes:
serializeClass(classNode)
catch
case e: java.lang.RuntimeException if e.getMessage != null && e.getMessage.nn.contains("too large!") =>
backendReporting.error(
s"Could not write class ${classNode.name} because it exceeds JVM code size limits. ${e.getMessage}",
NoSourcePosition
)
backendReporting.error(s"Could not write class ${classNode.name} because it exceeds JVM code size limits. ${e.getMessage}")
null
case ex: Throwable =>
ex.printStackTrace()
backendReporting.error(s"Error while emitting ${classNode.name}\n${ex.getMessage}",NoSourcePosition)
backendReporting.error(s"Error while emitting ${classNode.name}\n${ex.getMessage}")
null

if (bytes != null) {
if (AsmUtils.traceSerializedClassEnabled && classNode.name.nn.contains(AsmUtils.traceSerializedClassPattern))
AsmUtils.traceClass(bytes)

val clsFile = classfileWriter.write(classNode.name.nn, bytes, sourceFile)
val clsFile = classfileWriter.writeClass(classNode.name.nn, bytes, sourceFile)
if clsFile != null then onFileCreated(clsFile)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dotty.tools.backend.jvm

import scala.collection.mutable.Clearable
import scala.collection.mutable.{Clearable, HashSet}
import dotty.tools.dotc.util.*
import dotty.tools.io.AbstractFile
import java.util.{Collection => JCollection, Map => JMap}
Expand All @@ -20,7 +20,7 @@ sealed abstract class PostProcessorFrontendAccess {
def getEntryPoints: List[String]

private val frontendLock: AnyRef = new Object()
@inline final def frontendSynch[T](x: => T): T = frontendLock.synchronized(x)
inline final def frontendSynch[T](inline x: => T): T = frontendLock.synchronized(x)
}

object PostProcessorFrontendAccess {
Expand All @@ -35,11 +35,12 @@ object PostProcessorFrontendAccess {
}

sealed trait BackendReporting {
def error(message: String, pos: SourcePosition): Unit
def error(message: String): Unit
def warning(message: String): Unit
def log(message: String): Unit
}

class Impl[I <: DottyBackendInterface](val int: I) extends PostProcessorFrontendAccess {
class Impl[I <: DottyBackendInterface](val int: I, entryPoints: HashSet[String]) extends PostProcessorFrontendAccess {
import int.given
lazy val compilerSettings: CompilerSettings = buildCompilerSettings()

Expand Down Expand Up @@ -67,13 +68,11 @@ object PostProcessorFrontendAccess {
}

object backendReporting extends BackendReporting {
def error(message: String, pos: SourcePosition): Unit = frontendSynch(report.error(message, pos))
def error(message: String): Unit = frontendSynch(report.error(message, NoSourcePosition))
def warning(message: String): Unit = frontendSynch(report.warning(message, NoSourcePosition))
def log(message: String): Unit = frontendSynch(report.log(message))
}

def getEntryPoints: List[String] = frontendSynch(Phases.genBCodePhase match {
case genBCode: GenBCode => genBCode.entryPoints.toList
case _ => Nil
})
def getEntryPoints: List[String] = frontendSynch(entryPoints.toList)
}
}

0 comments on commit fc9b098

Please sign in to comment.