Skip to content

Commit b51f668

Browse files
committed
Use JarWriter only when target jar is empty
1 parent d51279e commit b51f668

File tree

4 files changed

+60
-61
lines changed

4 files changed

+60
-61
lines changed

Diff for: compiler/src/dotty/tools/backend/jvm/ClassfileWriter.scala

+45-41
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,34 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
2020
private val dumpOutputDir: AbstractFile = getDirectoryOrNull(compilerSettings.dumpClassesDirectory)
2121

2222
// if non-null, classfiles are written to a jar instead of the output directory
23-
private val jarWriter: JarWriter =
24-
val f = compilerSettings.outputDirectory
25-
if f.hasExtension("jar") then
26-
// If no main class was specified, see if there's only one
27-
// entry point among the classes going into the jar.
28-
val mainClass = compilerSettings.mainClass match {
29-
case c @ Some(m) =>
30-
backendReporting.log(s"Main-Class was specified: ${m}")
31-
c
32-
case None => frontendAccess.getEntryPoints match {
33-
case Nil =>
34-
backendReporting.log("No Main-Class designated or discovered.")
35-
None
23+
private val jarWriter: JarWriter | Null = compilerSettings.outputDirectory match {
24+
case jar: JarArchive =>
25+
val mainClass = compilerSettings.mainClass.orElse {
26+
// If no main class was specified, see if there's only one
27+
// entry point among the classes going into the jar.
28+
frontendAccess.getEntryPoints match {
3629
case name :: Nil =>
3730
backendReporting.log(s"Unique entry point: setting Main-Class to $name")
3831
Some(name)
3932
case names =>
40-
backendReporting.log(s"No Main-Class due to multiple entry points:\n ${names.mkString("\n ")}")
33+
if names.isEmpty then backendReporting.warning("No Main-Class designated or discovered.")
34+
else backendReporting.warning(s"No Main-Class due to multiple entry points:\n ${names.mkString("\n ")}")
4135
None
4236
}
4337
}
44-
45-
val jarMainAttrs = mainClass.map(c => Name.MAIN_CLASS -> c).toList
46-
new Jar(f.file).jarWriter(jarMainAttrs: _*)
47-
else null
38+
jar.underlyingSource.map{ source =>
39+
if jar.isEmpty then
40+
val jarMainAttrs = mainClass.map(Name.MAIN_CLASS -> _).toList
41+
new Jar(source.file).jarWriter(jarMainAttrs: _*)
42+
else
43+
// Writing to non-empty JAR might be an undefined behaviour, e.g. in case if other files where
44+
// created using `AbstractFile.bufferedOutputStream`instead of JarWritter
45+
backendReporting.warning(s"Tried to write to non-empty JAR: $source")
46+
null
47+
}.orNull
48+
49+
case _ => null
50+
}
4851

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

91-
def write(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): AbstractFile | Null = try {
94+
def writeClass(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): AbstractFile | Null = try {
9295
// val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer)
93-
val outFile = if (jarWriter == null) {
94-
val outFolder = compilerSettings.outputDirectory
95-
val outFile = getFile(outFolder, className, ".class")
96-
writeBytes(outFile, bytes)
97-
outFile
98-
} else {
99-
val path = className + ".class"
100-
val out = jarWriter.newOutputStream(path)
101-
try out.write(bytes, 0, bytes.length)
102-
finally out.flush()
103-
null
104-
}
96+
val outFile = writeToJarOrFile(className, bytes, ".class")
10597
// Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart)
10698

10799
if (dumpOutputDir != null) {
@@ -111,22 +103,34 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
111103
outFile
112104
} catch {
113105
case e: FileConflictException =>
114-
backendReporting.error(s"error writing $className: ${e.getMessage}", NoSourcePosition)
106+
backendReporting.error(s"error writing $className: ${e.getMessage}")
115107
null
116108
case e: java.nio.file.FileSystemException =>
117109
if compilerSettings.debug then e.printStackTrace()
118-
backendReporting.error(s"error writing $className: ${e.getClass.getName} ${e.getMessage}", NoSourcePosition)
110+
backendReporting.error(s"error writing $className: ${e.getClass.getName} ${e.getMessage}")
119111
null
120112
}
121113

122-
def writeTasty(className: InternalName, bytes: Array[Byte]): Unit =
123-
val outFolder = compilerSettings.outputDirectory
124-
val outFile = getFile(outFolder, className, ".tasty")
125-
try writeBytes(outFile, bytes)
126-
catch case ex: ClosedByInterruptException =>
127-
try outFile.delete() // don't leave an empty or half-written tastyfile around after an interrupt
128-
catch case _: Throwable => ()
129-
finally throw ex
114+
def writeTasty(className: InternalName, bytes: Array[Byte]): Unit =
115+
writeToJarOrFile(className, bytes, ".tasty")
116+
117+
private def writeToJarOrFile(className: InternalName, bytes: Array[Byte], suffix: String): AbstractFile | Null = {
118+
if jarWriter == null then
119+
val outFolder = compilerSettings.outputDirectory
120+
val outFile = getFile(outFolder, className, suffix)
121+
try writeBytes(outFile, bytes)
122+
catch case ex: ClosedByInterruptException =>
123+
try outFile.delete() // don't leave an empty or half-written files around after an interrupt
124+
catch case _: Throwable => ()
125+
finally throw ex
126+
outFile
127+
else
128+
val path = className + suffix
129+
val out = jarWriter.newOutputStream(path)
130+
try out.write(bytes, 0, bytes.length)
131+
finally out.flush()
132+
null
133+
}
130134

131135
def close(): Unit = {
132136
if (jarWriter != null) jarWriter.close()

Diff for: compiler/src/dotty/tools/backend/jvm/GenBCode.scala

+3-4
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ class GenBCode extends Phase { self =>
2222
superCallsMap.update(sym, old + calls)
2323
}
2424

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

2928
private var _backendInterface: DottyBackendInterface = _
3029
def backendInterface(using Context): DottyBackendInterface = {
@@ -52,7 +51,7 @@ class GenBCode extends Phase { self =>
5251
private var _frontendAccess: PostProcessorFrontendAccess = _
5352
def frontendAccess(using Context): PostProcessorFrontendAccess = {
5453
if _frontendAccess eq null then
55-
_frontendAccess = PostProcessorFrontendAccess.Impl(backendInterface)
54+
_frontendAccess = PostProcessorFrontendAccess.Impl(backendInterface, entryPoints)
5655
_frontendAccess
5756
}
5857

Diff for: compiler/src/dotty/tools/backend/jvm/PostProcessor.scala

+4-7
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes:
1818
import int.given
1919

2020
val backendUtils = new BackendUtils(this)
21-
lazy val classfileWriter = ClassfileWriter(frontendAccess)
21+
val classfileWriter = ClassfileWriter(frontendAccess)
2222

2323
def postProcessAndSendToDisk(generatedDefs: GeneratedDefs): Unit = {
2424
val GeneratedDefs(classes, tasty) = generatedDefs
@@ -30,21 +30,18 @@ class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes:
3030
serializeClass(classNode)
3131
catch
3232
case e: java.lang.RuntimeException if e.getMessage != null && e.getMessage.nn.contains("too large!") =>
33-
backendReporting.error(
34-
s"Could not write class ${classNode.name} because it exceeds JVM code size limits. ${e.getMessage}",
35-
NoSourcePosition
36-
)
33+
backendReporting.error(s"Could not write class ${classNode.name} because it exceeds JVM code size limits. ${e.getMessage}")
3734
null
3835
case ex: Throwable =>
3936
ex.printStackTrace()
40-
backendReporting.error(s"Error while emitting ${classNode.name}\n${ex.getMessage}",NoSourcePosition)
37+
backendReporting.error(s"Error while emitting ${classNode.name}\n${ex.getMessage}")
4138
null
4239

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

47-
val clsFile = classfileWriter.write(classNode.name.nn, bytes, sourceFile)
44+
val clsFile = classfileWriter.writeClass(classNode.name.nn, bytes, sourceFile)
4845
if clsFile != null then onFileCreated(clsFile)
4946
}
5047
}

Diff for: compiler/src/dotty/tools/backend/jvm/PostProcessorFrontendAccess.scala

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dotty.tools.backend.jvm
22

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

2222
private val frontendLock: AnyRef = new Object()
23-
@inline final def frontendSynch[T](x: => T): T = frontendLock.synchronized(x)
23+
inline final def frontendSynch[T](inline x: => T): T = frontendLock.synchronized(x)
2424
}
2525

2626
object PostProcessorFrontendAccess {
@@ -35,11 +35,12 @@ object PostProcessorFrontendAccess {
3535
}
3636

3737
sealed trait BackendReporting {
38-
def error(message: String, pos: SourcePosition): Unit
38+
def error(message: String): Unit
39+
def warning(message: String): Unit
3940
def log(message: String): Unit
4041
}
4142

42-
class Impl[I <: DottyBackendInterface](val int: I) extends PostProcessorFrontendAccess {
43+
class Impl[I <: DottyBackendInterface](val int: I, entryPoints: HashSet[String]) extends PostProcessorFrontendAccess {
4344
import int.given
4445
lazy val compilerSettings: CompilerSettings = buildCompilerSettings()
4546

@@ -67,13 +68,11 @@ object PostProcessorFrontendAccess {
6768
}
6869

6970
object backendReporting extends BackendReporting {
70-
def error(message: String, pos: SourcePosition): Unit = frontendSynch(report.error(message, pos))
71+
def error(message: String): Unit = frontendSynch(report.error(message, NoSourcePosition))
72+
def warning(message: String): Unit = frontendSynch(report.warning(message, NoSourcePosition))
7173
def log(message: String): Unit = frontendSynch(report.log(message))
7274
}
7375

74-
def getEntryPoints: List[String] = frontendSynch(Phases.genBCodePhase match {
75-
case genBCode: GenBCode => genBCode.entryPoints.toList
76-
case _ => Nil
77-
})
76+
def getEntryPoints: List[String] = frontendSynch(entryPoints.toList)
7877
}
7978
}

0 commit comments

Comments
 (0)