diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 49f5ff79130b..ffae0c29ff7f 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -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 { @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index ac6af4dfd2c3..0602b770233a 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -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 @@ -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) @@ -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)) { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyHeaderUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyHeaderUnpickler.scala new file mode 100644 index 000000000000..d06436e5af77 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyHeaderUnpickler.scala @@ -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) +} diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index f3ae3292776f..94dd7f4b8120 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -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 @@ -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()) }