All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.tools.nsc.tasty.TastyUnpickler.scala Maven / Gradle / Ivy

/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala.tools.nsc.tasty

import scala.collection.mutable
import scala.tools.tasty.{ErasedTypeRef, Signature, TastyFormat, TastyHeaderUnpickler, TastyName, TastyReader, TastyRefs}
import TastyFormat.NameTags._
import TastyRefs.NameRef
import TastyName._
import scala.reflect.io.AbstractFile

/**The entry point to TASTy unpickling for nsc, initialises a `TastyUniverse#Context` with the root symbols of a
 * top-level class, then parses the header and names from a TASTy file, before entering symbols from the `ASTs` section
 * with `TreeUnpickler`
 */
object TastyUnpickler {

  /** Unpickle symbol table information descending from a class and/or singleton object root
   *  from an array of bytes.
   *  @param tasty      the interface that translates TASTy operations into symbol table operations
   *  @param classRoot  the top-level class which is unpickled
   *  @param objectRoot the top-level singleton object which is unpickled
   *  @param filename   filename associated with bytearray, only used for error messages
   */
  def unpickle[Tasty <: TastyUniverse](tasty: Tasty)(bytes: Array[Byte], classRoot: tasty.Symbol, objectRoot: tasty.Symbol, filename: String): Unit = {
    import tasty._
    implicit val ctx: Context = new InitialContext(classRoot, AbstractFile.getFile(filename))

    ctx.log(s"Unpickling $filename")

    val unpickler = new TastyUnpickler[tasty.type](new TastyReader(bytes))(tasty)
    unpickler.readHeader()
    unpickler.readNames()
    val Some(astReader) = unpickler.readSection(TastyFormat.ASTsSection): @unchecked
    val treeUnpickler = new TreeUnpickler[tasty.type](astReader, unpickler.nameAtRef)(tasty)
    treeUnpickler.enterTopLevel(classRoot, objectRoot)
  }

  private final class Table[T] extends (NameRef => T) {
    private[this] val names = new mutable.ArrayBuffer[T]
    def add(name: T): mutable.ArrayBuffer[T] = names += name
    def apply(ref: NameRef): T = names(ref.index)
    def size: Int = names.size
  }
}

import TastyUnpickler._

private class TastyUnpickler[Tasty <: TastyUniverse](reader: TastyReader)(implicit tasty: Tasty) { self =>
  import tasty.{Context, assert}
  import reader._

  private[this] val nameTable = new Table[TastyName]

  def nameAtRef: NameRef => TastyName = nameTable

  private def readName(): TastyName = nameTable(readNameRef())

  private def readParamSig(): Signature.ParamSig[ErasedTypeRef] = {
    val ref = readInt()
    if (ref < 0)
      Left(ref.abs)
    else {
      Right(ErasedTypeRef(nameTable(NameRef(ref))))
    }
  }

  private def readNameContents()(implicit ctx: Context): TastyName = {
    val tag = readByte()
    val length = readNat()
    val start = currentAddr
    val end = start + length
    def debugName(name: TastyName): name.type = {
      ctx.log(s"${nameTable.size}: ${name.debug}")
      name
    }
    def readSignedRest(original: TastyName, target: TastyName): TastyName = {
      val result = ErasedTypeRef(readName())
      val paramsSig = until(end)(readParamSig())
      val sig = Signature(paramsSig, result)
      debugName(SignedName(original, sig, target))
    }
    val result = tag match {
      case UTF8 =>
        goto(end)
        debugName(SimpleName(new String(bytes.slice(start.index, start.index + length), "UTF-8")))
      case tag @ (QUALIFIED | EXPANDED | EXPANDPREFIX) =>
        val sep = tag match {
          case QUALIFIED    => PathSep
          case EXPANDED     => ExpandedSep
          case EXPANDPREFIX => ExpandPrefixSep
        }
        debugName(QualifiedName(readName(), sep, readName().asSimpleName))
      case UNIQUE =>
        val separator = readName().asSimpleName
        val num       = readNat()
        val originals = until(end)(readName())
        val original = if (originals.isEmpty) TastyName.Empty else originals.head
        debugName(UniqueName(original, separator, num))
      case DEFAULTGETTER =>
        debugName(DefaultName(readName(), readNat()))
      case TARGETSIGNED =>
        val original = readName()
        val target = readName()
        readSignedRest(original, target)
      case SIGNED =>
        val original = readName()
        readSignedRest(original, original)
      case OBJECTCLASS =>
        debugName(ObjectName(readName()))
      case BODYRETAINER =>
        debugName(SuffixName(readName(), BodyRetainerSuffix))
      case INLINEACCESSOR | SUPERACCESSOR =>
        val prefix = tag match {
          case INLINEACCESSOR => InlinePrefix
          case SUPERACCESSOR  => SuperPrefix
        }
        debugName(PrefixName(prefix, readName()))
      case _ =>
        val qual = readName()
        sys.error(s"at NameRef(${nameTable.size}): name `${qual.debug}` is qualified by unknown tag $tag")
    }
    assert(currentAddr == end, s"bad name ${result.debug} $start $currentAddr $end")
    result
  }

  def readHeader(): Unit = new TastyHeaderUnpickler(reader).readHeader()

  def readNames()(implicit ctx: Context): Unit = {
    ctx.log(s"reading names:")
    doUntil(readEnd()) { nameTable.add(readNameContents()) }
  }

  def readSection(name: String): Option[TastyReader] = {
    while (!isAtEnd) {
      val secName = readName().asSimpleName.raw
      val secEnd  = readEnd()
      val curr    = currentAddr
      goto(secEnd)
      if (name == secName) return Some(new TastyReader(bytes, curr.index, secEnd.index, curr.index))
    }
    None
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy