package dotty.tools.dotc
package core
package tasty
import scala.collection.mutable
import TastyFormat._
import TastyBuffer.NameRef
import Names.{Name, TermName, termName, EmptyTermName}
import NameKinds._
import java.util.UUID
object TastyUnpickler {
class UnpickleException(msg: String) extends Exception(msg)
abstract class SectionUnpickler[R](val name: String) {
def unpickle(reader: TastyReader, nameAtRef: NameTable): R
}
class NameTable extends (NameRef => TermName) {
private val names = new mutable.ArrayBuffer[TermName]
def add(name: TermName) = names += name
def apply(ref: NameRef) = names(ref.index)
def contents: Iterable[TermName] = names
}
}
import TastyUnpickler._
class TastyUnpickler(reader: TastyReader) {
import reader._
def this(bytes: Array[Byte]) = this(new TastyReader(bytes))
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
private def readNameContents(): TermName = {
val tag = readByte()
val length = readNat()
val start = currentAddr
val end = start + length
val result = tag match {
case UTF8 =>
goto(end)
termName(bytes, start.index, length)
case QUALIFIED | FLATTENED | EXPANDED | EXPANDPREFIX =>
qualifiedNameKindOfTag(tag)(readName(), readName().asSimpleName)
case UNIQUE =>
val separator = readName().toString
val num = readNat()
val originals = until(end)(readName())
val original = if (originals.isEmpty) EmptyTermName else originals.head
uniqueNameKindOfSeparator(separator)(original, num)
case DEFAULTGETTER =>
DefaultGetterName(readName(), readNat())
case VARIANT =>
VariantName(readName(), readNat() - 1)
case OUTERSELECT =>
OuterSelectName(readName(), readNat())
case SIGNED =>
val original = readName()
val result = readName().toTypeName
val params = until(end)(readName().toTypeName)
var sig = Signature(params, result)
if (sig == Signature.NotAMethod) sig = Signature.NotAMethod
SignedName(original, sig)
case _ =>
simpleNameKindOfTag(tag)(readName())
}
assert(currentAddr == end, s"bad name $result $start $currentAddr $end")
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()
locally {
until(readEnd()) { nameAtRef.add(readNameContents()) }
while (!isAtEnd) {
val secName = readString()
val secEnd = readEnd()
sectionReader(secName) = new TastyReader(bytes, currentAddr.index, secEnd.index, currentAddr.index)
goto(secEnd)
}
}
def unpickle[R](sec: SectionUnpickler[R]): Option[R] =
for (reader <- sectionReader.get(sec.name)) yield
sec.unpickle(reader, nameAtRef)
}