Skip to content

Commit 8305bd0

Browse files
Merge pull request #7918 from dotty-staging/fix-stale-tasty
Fix #7852: avoid reading stale .tasty files from jars
2 parents 21a5608 + 9df2d1b commit 8305bd0

File tree

11 files changed

+86
-34
lines changed

11 files changed

+86
-34
lines changed

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

+29-22
Original file line numberDiff line numberDiff line change
@@ -774,35 +774,42 @@ 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-
val path = classfile.path.stripSuffix(".class") + ".tasty"
784-
val stream = cl.getResourceAsStream(path)
785-
if (stream != null) {
786-
val tastyOutStream = new ByteArrayOutputStream()
787-
val buffer = new Array[Byte](1024)
788-
var read = stream.read(buffer, 0, buffer.length)
789-
while (read != -1) {
790-
tastyOutStream.write(buffer, 0, read)
791-
read = stream.read(buffer, 0, buffer.length)
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 {
785+
val tastyOutStream = new ByteArrayOutputStream()
786+
val buffer = new Array[Byte](1024)
787+
var read = stream.read(buffer, 0, buffer.length)
788+
while (read != -1) {
789+
tastyOutStream.write(buffer, 0, read)
790+
read = stream.read(buffer, 0, buffer.length)
791+
}
792+
tastyOutStream.flush()
793+
tastyOutStream.toByteArray
794+
} finally {
795+
stream.close()
792796
}
793-
tastyOutStream.flush()
794-
tastyOutStream.toByteArray
795797
}
796798
else {
797-
ctx.error(s"Could not find $path in $jar")
799+
ctx.error(s"Could not find $path in ${classfile.underlyingSource}")
798800
Array.empty
799801
}
800802
case _ =>
801-
val plainFile = new PlainFile(io.File(classfile.jpath).changeExtension("tasty"))
802-
if (plainFile.exists) plainFile.toByteArray
803-
else {
804-
ctx.error("Could not find " + plainFile)
803+
if (classfile.jpath == null) {
804+
ctx.error("Could not load TASTY from .tasty for virtual file " + classfile)
805805
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+
}
806813
}
807814
}
808815
if (tastyBytes.nonEmpty) {

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

+15-12
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class A
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class B {
2+
val x = new A
3+
}
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
lazy val a = Project("a", file("a"))
2+
.settings(
3+
exportJars := true
4+
)
5+
6+
lazy val b = Project("b", file("b"))
7+
.dependsOn(a)
8+
.settings(
9+
exportJars := true
10+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
class A
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
class B {
3+
val x = new A
4+
}
5+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion"),
10+
scalacOptions += "-language:Scala2Compat"
11+
)
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
> compile
2+
$ copy-file changes/A1.scala a/A.scala
3+
$ copy-file changes/B1.scala b/B.scala
4+
# This used to fail with "Tasty UUID (...) file did not correspond the tasty UUID (...) declared in the classfile"
5+
# because we were somehow reading the .tasty file from the previous compilation run, even though it does not exist
6+
# on disk anymore (#7852)
7+
> compile

0 commit comments

Comments
 (0)