Skip to content

Commit e6110c4

Browse files
Merge pull request #4809 from dotty-staging/check-tasty-hash
Check tasty file hash
2 parents cc5057c + e39b726 commit e6110c4

File tree

4 files changed

+60
-26
lines changed

4 files changed

+60
-26
lines changed

compiler/src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import scala.tools.asm
2626
import scala.tools.asm.tree._
2727
import tpd._
2828
import StdNames._
29+
import dotty.tools.dotc.core.tasty.{TastyBuffer, TastyHeaderUnpickler, TastyPickler}
2930
import dotty.tools.io._
3031

3132
class GenBCode extends Phase {
@@ -221,9 +222,17 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
221222
val outstream = new DataOutputStream(outTastyFile.bufferedOutput)
222223
try outstream.write(binary)
223224
finally outstream.close()
224-
// TASTY attribute is created but 0 bytes are stored in it.
225-
// A TASTY attribute has length 0 if and only if the .tasty file exists.
226-
Array.empty[Byte]
225+
226+
val uuid = new TastyHeaderUnpickler(binary).readHeader()
227+
val lo = uuid.getMostSignificantBits
228+
val hi = uuid.getLeastSignificantBits
229+
230+
// TASTY attribute is created but only the UUID bytes are stored in it.
231+
// A TASTY attribute has length 16 if and only if the .tasty file exists.
232+
val buffer = new TastyBuffer(16)
233+
buffer.writeUncompressedLong(lo)
234+
buffer.writeUncompressedLong(hi)
235+
buffer.bytes
227236
} else {
228237
// Create an empty file to signal that a tasty section exist in the corresponding .class
229238
// This is much cheaper and simpler to check than doing classfile parsing

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ package classfile
55

66
import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._
77
import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._
8-
import NameKinds.{ModuleClassName, DefaultGetterName}
8+
import NameKinds.DefaultGetterName
9+
import dotty.tools.dotc.core.tasty.{TastyHeaderUnpickler, TastyReader}
910
import ast.tpd._
10-
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, File, IOException }
11-
import java.nio
11+
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, IOException }
12+
1213
import java.lang.Integer.toHexString
1314
import java.net.URLClassLoader
15+
import java.util.UUID
1416

15-
import scala.collection.{ mutable, immutable }
17+
import scala.collection.immutable
1618
import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
1719
import scala.annotation.switch
1820
import typer.Checking.checkNonCyclic
@@ -785,7 +787,8 @@ class ClassfileParser(
785787

786788
if (scan(tpnme.TASTYATTR)) {
787789
val attrLen = in.nextInt
788-
if (attrLen == 0) { // A tasty attribute implies the existence of the .tasty file
790+
val bytes = in.nextBytes(attrLen)
791+
if (attrLen == 16) { // A tasty attribute with that has only a UUID (16 bytes) implies the existence of the .tasty file
789792
val tastyBytes: Array[Byte] = classfile.underlyingSource match { // TODO: simplify when #3552 is fixed
790793
case None =>
791794
ctx.error("Could not load TASTY from .tasty for virtual file " + classfile)
@@ -816,10 +819,16 @@ class ClassfileParser(
816819
Array.empty
817820
}
818821
}
819-
if (tastyBytes.nonEmpty)
822+
if (tastyBytes.nonEmpty) {
823+
val reader = new TastyReader(bytes, 0, 16)
824+
val expectedUUID = new UUID(reader.readUncompressedLong(), reader.readUncompressedLong())
825+
val tastyUUID = new TastyHeaderUnpickler(tastyBytes).readHeader()
826+
if (expectedUUID != tastyUUID)
827+
ctx.error(s"Tasty UUID ($tastyUUID) file did not correspond the tasty UUID declared in the classfile ($expectedUUID).")
820828
return unpickleTASTY(tastyBytes)
829+
}
821830
}
822-
else return unpickleTASTY(in.nextBytes(attrLen))
831+
else return unpickleTASTY(bytes)
823832
}
824833

825834
if (scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name)) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dotty.tools.dotc
2+
package core
3+
package tasty
4+
5+
import java.util.UUID
6+
7+
import dotty.tools.dotc.core.tasty.TastyFormat.{MajorVersion, MinorVersion, header}
8+
import dotty.tools.dotc.core.tasty.TastyUnpickler.UnpickleException
9+
10+
class TastyHeaderUnpickler(reader: TastyReader) {
11+
import reader._
12+
13+
def this(bytes: Array[Byte]) = this(new TastyReader(bytes))
14+
15+
def readHeader(): UUID = {
16+
for (i <- 0 until header.length)
17+
check(readByte() == header(i), "not a TASTy file")
18+
val major = readNat()
19+
val minor = readNat()
20+
check(major == MajorVersion && minor <= MinorVersion,
21+
s"""TASTy signature has wrong version.
22+
| expected: $MajorVersion.$MinorVersion
23+
| found : $major.$minor""".stripMargin)
24+
new UUID(readUncompressedLong(), readUncompressedLong())
25+
}
26+
27+
def isAtEnd: Boolean = reader.isAtEnd
28+
29+
private def check(cond: Boolean, msg: => String): Unit =
30+
if (!cond) throw new UnpickleException(msg)
31+
}

compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ class TastyUnpickler(reader: TastyReader) {
3535
private val sectionReader = new mutable.HashMap[String, TastyReader]
3636
val nameAtRef = new NameTable
3737

38-
private def check(cond: Boolean, msg: => String) =
39-
if (!cond) throw new UnpickleException(msg)
40-
4138
private def readName(): TermName = nameAtRef(readNameRef())
4239
private def readString(): String = readName().toString
4340

@@ -73,19 +70,7 @@ class TastyUnpickler(reader: TastyReader) {
7370
result
7471
}
7572

76-
private def readHeader(): UUID = {
77-
for (i <- 0 until header.length)
78-
check(readByte() == header(i), "not a TASTy file")
79-
val major = readNat()
80-
val minor = readNat()
81-
check(major == MajorVersion && minor <= MinorVersion,
82-
s"""TASTy signature has wrong version.
83-
| expected: $MajorVersion.$MinorVersion
84-
| found : $major.$minor""".stripMargin)
85-
new UUID(readUncompressedLong(), readUncompressedLong())
86-
}
87-
88-
private val uuid = readHeader()
73+
new TastyHeaderUnpickler(reader).readHeader()
8974

9075
locally {
9176
until(readEnd()) { nameAtRef.add(readNameContents()) }

0 commit comments

Comments
 (0)