Skip to content

Check tasty file hash #4809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import scala.tools.asm
import scala.tools.asm.tree._
import tpd._
import StdNames._
import dotty.tools.dotc.core.tasty.{TastyBuffer, TastyHeaderUnpickler, TastyPickler}
import dotty.tools.io._

class GenBCode extends Phase {
Expand Down Expand Up @@ -221,9 +222,17 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
val outstream = new DataOutputStream(outTastyFile.bufferedOutput)
try outstream.write(binary)
finally outstream.close()
// TASTY attribute is created but 0 bytes are stored in it.
// A TASTY attribute has length 0 if and only if the .tasty file exists.
Array.empty[Byte]

val uuid = new TastyHeaderUnpickler(binary).readHeader()
val lo = uuid.getMostSignificantBits
val hi = uuid.getLeastSignificantBits

// TASTY attribute is created but only the UUID bytes are stored in it.
// A TASTY attribute has length 16 if and only if the .tasty file exists.
val buffer = new TastyBuffer(16)
buffer.writeUncompressedLong(lo)
buffer.writeUncompressedLong(hi)
buffer.bytes
} else {
// Create an empty file to signal that a tasty section exist in the corresponding .class
// This is much cheaper and simpler to check than doing classfile parsing
Expand Down
23 changes: 16 additions & 7 deletions compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ package classfile

import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._
import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._
import NameKinds.{ModuleClassName, DefaultGetterName}
import NameKinds.DefaultGetterName
import dotty.tools.dotc.core.tasty.{TastyHeaderUnpickler, TastyReader}
import ast.tpd._
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, File, IOException }
import java.nio
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, IOException }

import java.lang.Integer.toHexString
import java.net.URLClassLoader
import java.util.UUID

import scala.collection.{ mutable, immutable }
import scala.collection.immutable
import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
import scala.annotation.switch
import typer.Checking.checkNonCyclic
Expand Down Expand Up @@ -785,7 +787,8 @@ class ClassfileParser(

if (scan(tpnme.TASTYATTR)) {
val attrLen = in.nextInt
if (attrLen == 0) { // A tasty attribute implies the existence of the .tasty file
val bytes = in.nextBytes(attrLen)
if (attrLen == 16) { // A tasty attribute with that has only a UUID (16 bytes) implies the existence of the .tasty file
val tastyBytes: Array[Byte] = classfile.underlyingSource match { // TODO: simplify when #3552 is fixed
case None =>
ctx.error("Could not load TASTY from .tasty for virtual file " + classfile)
Expand Down Expand Up @@ -816,10 +819,16 @@ class ClassfileParser(
Array.empty
}
}
if (tastyBytes.nonEmpty)
if (tastyBytes.nonEmpty) {
val reader = new TastyReader(bytes, 0, 16)
val expectedUUID = new UUID(reader.readUncompressedLong(), reader.readUncompressedLong())
val tastyUUID = new TastyHeaderUnpickler(tastyBytes).readHeader()
if (expectedUUID != tastyUUID)
ctx.error(s"Tasty UUID ($tastyUUID) file did not correspond the tasty UUID declared in the classfile ($expectedUUID).")
return unpickleTASTY(tastyBytes)
}
}
else return unpickleTASTY(in.nextBytes(attrLen))
else return unpickleTASTY(bytes)
}

if (scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dotty.tools.dotc
package core
package tasty

import java.util.UUID

import dotty.tools.dotc.core.tasty.TastyFormat.{MajorVersion, MinorVersion, header}
import dotty.tools.dotc.core.tasty.TastyUnpickler.UnpickleException

class TastyHeaderUnpickler(reader: TastyReader) {
import reader._

def this(bytes: Array[Byte]) = this(new TastyReader(bytes))

def readHeader(): UUID = {
for (i <- 0 until header.length)
check(readByte() == header(i), "not a TASTy file")
val major = readNat()
val minor = readNat()
check(major == MajorVersion && minor <= MinorVersion,
s"""TASTy signature has wrong version.
| expected: $MajorVersion.$MinorVersion
| found : $major.$minor""".stripMargin)
new UUID(readUncompressedLong(), readUncompressedLong())
}

def isAtEnd: Boolean = reader.isAtEnd

private def check(cond: Boolean, msg: => String): Unit =
if (!cond) throw new UnpickleException(msg)
}
17 changes: 1 addition & 16 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ class TastyUnpickler(reader: TastyReader) {
private val sectionReader = new mutable.HashMap[String, TastyReader]
val nameAtRef = new NameTable

private def check(cond: Boolean, msg: => String) =
if (!cond) throw new UnpickleException(msg)

private def readName(): TermName = nameAtRef(readNameRef())
private def readString(): String = readName().toString

Expand Down Expand Up @@ -73,19 +70,7 @@ class TastyUnpickler(reader: TastyReader) {
result
}

private def readHeader(): UUID = {
for (i <- 0 until header.length)
check(readByte() == header(i), "not a TASTy file")
val major = readNat()
val minor = readNat()
check(major == MajorVersion && minor <= MinorVersion,
s"""TASTy signature has wrong version.
| expected: $MajorVersion.$MinorVersion
| found : $major.$minor""".stripMargin)
new UUID(readUncompressedLong(), readUncompressedLong())
}

private val uuid = readHeader()
new TastyHeaderUnpickler(reader).readHeader()

locally {
until(readEnd()) { nameAtRef.add(readNameContents()) }
Expand Down