Skip to content

Commit 9df2d1b

Browse files
committed
Don't use a classloader to read .tasty files from jars
The previous commit tried to fix some weird caching behaviors by closing the classloader used to read a .tasty file, but it turns out the JVM is dumb and that breaks any other in-use classloader that uses the same jar. Give up on the whole thing and work directly with the underlying ZipArchive to find the .tasty file (this just required adding a "parent" field to ZipArchive#Entry).
1 parent d810ffc commit 9df2d1b

File tree

2 files changed

+37
-34
lines changed

2 files changed

+37
-34
lines changed

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -774,16 +774,14 @@ class ClassfileParser(
774774
val attrLen = in.nextInt
775775
val bytes = in.nextBytes(attrLen)
776776
if (attrLen == 16) { // A tasty attribute with that has only a UUID (16 bytes) implies the existence of the .tasty file
777-
val tastyBytes: Array[Byte] = classfile.underlyingSource match { // TODO: simplify when #3552 is fixed
778-
case None =>
779-
ctx.error("Could not load TASTY from .tasty for virtual file " + classfile)
780-
Array.empty
781-
case Some(jar: ZipArchive) => // We are in a jar
782-
val cl = new URLClassLoader(Array(jar.jpath.toUri.toURL), /*parent =*/ null)
783-
try {
784-
val path = classfile.path.stripSuffix(".class") + ".tasty"
785-
val stream = cl.getResourceAsStream(path)
786-
if (stream != null) {
777+
val tastyBytes: Array[Byte] = classfile match { // TODO: simplify when #3552 is fixed
778+
case classfile: io.ZipArchive#Entry => // We are in a jar
779+
val path = classfile.parent.lookupName(
780+
classfile.name.stripSuffix(".class") + ".tasty", directory = false
781+
)
782+
if (path != null) {
783+
val stream = path.input
784+
try {
787785
val tastyOutStream = new ByteArrayOutputStream()
788786
val buffer = new Array[Byte](1024)
789787
var read = stream.read(buffer, 0, buffer.length)
@@ -793,23 +791,25 @@ class ClassfileParser(
793791
}
794792
tastyOutStream.flush()
795793
tastyOutStream.toByteArray
796-
}
797-
else {
798-
ctx.error(s"Could not find $path in $jar")
799-
Array.empty
794+
} finally {
795+
stream.close()
800796
}
801797
}
802-
finally {
803-
// If we don't close the classloader, spooky things happen (see
804-
// scripted test source-dependencies/export-jars2).
805-
cl.close()
798+
else {
799+
ctx.error(s"Could not find $path in ${classfile.underlyingSource}")
800+
Array.empty
806801
}
807802
case _ =>
808-
val plainFile = new PlainFile(io.File(classfile.jpath).changeExtension("tasty"))
809-
if (plainFile.exists) plainFile.toByteArray
810-
else {
811-
ctx.error("Could not find " + plainFile)
803+
if (classfile.jpath == null) {
804+
ctx.error("Could not load TASTY from .tasty for virtual file " + classfile)
812805
Array.empty
806+
} else {
807+
val plainFile = new PlainFile(io.File(classfile.jpath).changeExtension("tasty"))
808+
if (plainFile.exists) plainFile.toByteArray
809+
else {
810+
ctx.error("Could not find " + plainFile)
811+
Array.empty
812+
}
813813
}
814814
}
815815
if (tastyBytes.nonEmpty) {

compiler/src/dotty/tools/io/ZipArchive.scala

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
6666
def absolute: AbstractFile = unsupported()
6767

6868
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
69-
sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) {
69+
sealed abstract class Entry(path: String, val parent: Entry) extends VirtualFile(baseName(path), path) {
7070
// have to keep this name for compat with sbt's compiler-interface
7171
def getArchive: ZipFile = null
7272
override def underlyingSource: Option[ZipArchive] = Some(self)
7373
override def toString: String = self.path + "(" + path + ")"
7474
}
7575

7676
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
77-
class DirEntry(path: String) extends Entry(path) {
77+
class DirEntry(path: String, parent: Entry) extends Entry(path, parent) {
7878
val entries: mutable.HashMap[String, Entry] = mutable.HashMap()
7979

8080
override def isDirectory: Boolean = true
@@ -98,7 +98,7 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
9898
case Some(v) => v
9999
case None =>
100100
val parent = ensureDir(dirs, dirName(path), null)
101-
val dir = new DirEntry(path)
101+
val dir = new DirEntry(path, parent)
102102
parent.entries(baseName(path)) = dir
103103
dirs(path) = dir
104104
dir
@@ -120,8 +120,9 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
120120
private class LazyEntry(
121121
name: String,
122122
time: Long,
123-
size: Int
124-
) extends Entry(name) {
123+
size: Int,
124+
parent: DirEntry
125+
) extends Entry(name, parent) {
125126
override def lastModified: Long = time // could be stale
126127
override def input: InputStream = {
127128
val zipFile = openZipFile()
@@ -140,15 +141,16 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
140141
// faster than LazyEntry.
141142
private class LeakyEntry(
142143
zipFile: ZipFile,
143-
zipEntry: ZipEntry
144-
) extends Entry(zipEntry.getName) {
144+
zipEntry: ZipEntry,
145+
parent: DirEntry
146+
) extends Entry(zipEntry.getName, parent) {
145147
override def lastModified: Long = zipEntry.getTime
146148
override def input: InputStream = zipFile.getInputStream(zipEntry)
147149
override def sizeOption: Option[Int] = Some(zipEntry.getSize.toInt)
148150
}
149151

150152
@volatile lazy val (root, allDirs): (DirEntry, collection.Map[String, DirEntry]) = {
151-
val root = new DirEntry("/")
153+
val root = new DirEntry("/", null)
152154
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
153155
val zipFile = openZipFile()
154156
val entries = zipFile.entries()
@@ -163,10 +165,11 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
163165
new LazyEntry(
164166
zipEntry.getName(),
165167
zipEntry.getTime(),
166-
zipEntry.getSize().toInt
168+
zipEntry.getSize().toInt,
169+
dir
167170
)
168171
else
169-
new LeakyEntry(zipFile, zipEntry)
172+
new LeakyEntry(zipFile, zipEntry, dir)
170173

171174
dir.entries(f.name) = f
172175
}
@@ -195,15 +198,15 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
195198

196199
final class ManifestResources(val url: URL) extends ZipArchive(null) {
197200
def iterator(): Iterator[AbstractFile] = {
198-
val root = new DirEntry("/")
201+
val root = new DirEntry("/", null)
199202
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
200203
val manifest = new Manifest(input)
201204
val iter = manifest.getEntries().keySet().iterator().asScala.filter(_.endsWith(".class")).map(new ZipEntry(_))
202205

203206
for (zipEntry <- iter) {
204207
val dir = getDir(dirs, zipEntry)
205208
if (!zipEntry.isDirectory) {
206-
val f = new Entry(zipEntry.getName) {
209+
val f = new Entry(zipEntry.getName, dir) {
207210
override def lastModified = zipEntry.getTime()
208211
override def input = resourceInputStream(path)
209212
override def sizeOption = None

0 commit comments

Comments
 (0)