alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (Pickler.scala)

This example Scala source code file (Pickler.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Scala source code examples by using tags.

All credit for the original source code belongs to scala-lang.org; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Scala tags/keywords

annotationinfo, anyref, collection, compiler, constant, list, modifiers, name, nosymbol, nsc, pickling, reflection, symbol, tree, unit, utilities

The Pickler.scala Scala example source code

/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package symtab
package classfile

import java.lang.Float.floatToIntBits
import java.lang.Double.doubleToLongBits
import scala.io.Codec
import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat }
import scala.reflect.internal.util.shortClassOfInstance
import scala.collection.mutable.LinkedHashMap
import PickleFormat._
import Flags._

/**
 * Serialize a top-level module and/or class.
 *
 * @see EntryTags.scala for symbol table attribute format.
 *
 * @author Martin Odersky
 * @version 1.0
 */
abstract class Pickler extends SubComponent {
  import global._

  val phaseName = "pickler"

  def newPhase(prev: Phase): StdPhase = new PicklePhase(prev)

  class PicklePhase(prev: Phase) extends StdPhase(prev) {
    def apply(unit: CompilationUnit) {
      def pickle(tree: Tree) {
        def add(sym: Symbol, pickle: Pickle) = {
          if (currentRun.compiles(sym) && !currentRun.symData.contains(sym)) {
            debuglog("pickling " + sym)
            pickle putSymbol sym
            currentRun.symData(sym) = pickle
          }
        }

        tree match {
          case PackageDef(_, stats) =>
            stats foreach pickle
          case ClassDef(_, _, _, _) | ModuleDef(_, _, _) =>
            val sym = tree.symbol
            val pickle = new Pickle(sym)
            add(sym, pickle)
            add(sym.companionSymbol, pickle)
            pickle.writeArray()
            currentRun registerPickle sym
          case _ =>
        }
      }

      try {
        pickle(unit.body)
      } catch {
        case e: FatalError =>
          for (t <- unit.body) {
            // If there are any erroneous types in the tree, then we will crash
            // when we pickle it: so let's report an error instead.  We know next
            // to nothing about what happened, but our supposition is a lot better
            // than "bad type: <error>" in terms of explanatory power.
            //
            // OPT: do this only as a recovery after fatal error. Checking in advance was expensive.
            if (t.isErroneous) {
              if (settings.debug) e.printStackTrace()
              unit.error(t.pos, "erroneous or inaccessible type")
              return
            }
          }
          throw e
      }
    }
  }

  private class Pickle(root: Symbol) extends PickleBuffer(new Array[Byte](4096), -1, 0) {
    private val rootName  = root.name.toTermName
    private val rootOwner = root.owner
    private var entries   = new Array[AnyRef](256)
    private var ep        = 0
    private val index     = new LinkedHashMap[AnyRef, Int]
    private lazy val nonClassRoot = findSymbol(root.ownersIterator)(!_.isClass)

    private def isRootSym(sym: Symbol) =
      sym.name.toTermName == rootName && sym.owner == rootOwner

    /** Returns usually symbol's owner, but picks classfile root instead
     *  for existentially bound variables that have a non-local owner.
     *  Question: Should this be done for refinement class symbols as well?
     *
     *  Note: tree pickling also finds its way here; e.g. in SI-7501 the pickling
     *  of trees in annotation arguments considers the parameter symbol of a method
     *  called in such a tree as "local". The condition `sym.isValueParameter` was
     *  added to fix that bug, but there may be a better way.
     */
    private def localizedOwner(sym: Symbol) =
      if (isLocalToPickle(sym) && !isRootSym(sym) && !isLocalToPickle(sym.owner))
        // don't use a class as the localized owner for type parameters that are not owned by a class: those are not instantiated by asSeenFrom
        // however, they would suddenly be considered by asSeenFrom if their localized owner became a class (causing the crashes of #4079, #2741)
        (if ((sym.isTypeParameter || sym.isValueParameter) && !sym.owner.isClass) nonClassRoot
         else root)
      else sym.owner

    /** Is root in symbol.owner*, or should it be treated as a local symbol
     *  anyway? This is the case if symbol is a refinement class,
     *  an existentially bound variable, or a higher-order type parameter.
     */
    private def isLocalToPickle(sym: Symbol): Boolean = (sym != NoSymbol) && !sym.isPackageClass && (
         isRootSym(sym)
      || sym.isRefinementClass
      || sym.isAbstractType && sym.hasFlag(EXISTENTIAL) // existential param
      || sym.isParameter
      || isLocalToPickle(sym.owner)
    )
    private def isExternalSymbol(sym: Symbol): Boolean = (sym != NoSymbol) && !isLocalToPickle(sym)

    // Phase 1 methods: Populate entries/index ------------------------------------

    /** Store entry e in index at next available position unless
     *  it is already there.
     *
     *  @return      true iff entry is new.
     */
    private def putEntry(entry: AnyRef): Boolean = index.get(entry) match {
      case Some(_) => false
      case None =>
        if (ep == entries.length) {
          val entries1 = new Array[AnyRef](ep * 2)
          System.arraycopy(entries, 0, entries1, 0, ep)
          entries = entries1
        }
        entries(ep) = entry
        index(entry) = ep
        ep = ep + 1
        true
    }

    private def deskolemizeTypeSymbols(ref: AnyRef): AnyRef = ref match {
      case sym: Symbol => deskolemize(sym)
      case _           => ref
    }

    /** If the symbol is a type skolem, deskolemize and log it.
     *  If we fail to deskolemize, in a method like
     *    trait Trait[+A] { def f[CC[X]] : CC[A] }
     *  the applied type CC[A] will hold a different CC symbol
     *  than the type-constructor type-parameter CC.
     */
    private def deskolemize(sym: Symbol): Symbol = {
      if (sym.isTypeSkolem) {
        val sym1 = sym.deSkolemize
        log({
          val what0 = sym.defString
          val what = sym1.defString match {
            case `what0` => what0
            case other   => what0 + "->" + other
          }
          val where = sym.enclMethod.fullLocationString
          s"deskolemizing $what in $where"
        })
        sym1
      }
      else sym
    }

    /** Store symbol in index. If symbol is local, also store everything it references.
     */
    def putSymbol(sym0: Symbol) {
      val sym = deskolemize(sym0)

      if (putEntry(sym)) {
        if (isLocalToPickle(sym)) {
          putEntry(sym.name)
          putSymbol(sym.owner)
          putSymbol(sym.privateWithin)
          putType(sym.info)
          if (sym.hasSelfType)
            putType(sym.typeOfThis)
          putSymbol(sym.alias)
          if (!sym.children.isEmpty) {
            val (locals, globals) = sym.children partition (_.isLocalClass)
            val children =
              if (locals.isEmpty) globals
              else globals + sym.newClassWithInfo(tpnme.LOCAL_CHILD, List(sym.tpe), EmptyScope, pos = sym.pos)

            putChildren(sym, children.toList sortBy (_.sealedSortName))
          }
          for (annot <- (sym.annotations filter (ann => ann.isStatic && !ann.isErroneous)).reverse)
            putAnnotation(sym, annot)
        }
        else if (sym != NoSymbol) {
          putEntry(if (sym.isModuleClass) sym.name.toTermName else sym.name)
          if (!sym.owner.isRoot) putSymbol(sym.owner)
        }
      }
    }

    private def putSymbols(syms: List[Symbol]) =
      syms foreach putSymbol

    /** Store type and everything it refers to in map index.
     */
    private def putType(tp: Type): Unit = if (putEntry(tp)) {
      tp match {
        case NoType | NoPrefix =>
          ;
        case ThisType(sym) =>
          putSymbol(sym)
        case SingleType(pre, sym) =>
          putType(pre)
          putSymbol(sym)
        case SuperType(thistpe, supertpe) =>
          putType(thistpe)
          putType(supertpe)
        case ConstantType(value) =>
          putConstant(value)
        case TypeRef(pre, sym, args) =>
          putType(pre)
          putSymbol(sym)
          putTypes(args)
        case TypeBounds(lo, hi) =>
          putType(lo)
          putType(hi)
        case tp: CompoundType =>
          putSymbol(tp.typeSymbol)
          putTypes(tp.parents)
          putSymbols(tp.decls.toList)
        case MethodType(params, restpe) =>
          putType(restpe)
          putSymbols(params)
        case NullaryMethodType(restpe) =>
          putType(restpe)
        case PolyType(tparams, restpe) =>
          putType(restpe)
          putSymbols(tparams)
        case ExistentialType(tparams, restpe) =>
          putType(restpe)
          putSymbols(tparams)
        case AnnotatedType(_, underlying) =>
          putType(underlying)
          tp.staticAnnotations foreach putAnnotation
        case _ =>
          throw new FatalError("bad type: " + tp + "(" + tp.getClass + ")")
      }
    }
    private def putTypes(tps: List[Type]) { tps foreach putType }

    private object putTreeTraverser extends Traverser {
      // Only used when pickling trees, i.e. in an argument of some Annotation
      // annotations in Modifiers are removed by the typechecker
      override def traverseModifiers(mods: Modifiers): Unit = if (putEntry(mods)) putEntry(mods.privateWithin)
      override def traverseName(name: Name): Unit           = putEntry(name)
      override def traverseConstant(const: Constant): Unit  = putEntry(const)
      override def traverse(tree: Tree): Unit               = putTree(tree)

      def put(tree: Tree): Unit = {
        if (tree.canHaveAttrs)
          putType(tree.tpe)
        if (tree.hasSymbolField)
          putSymbol(tree.symbol)

        super.traverse(tree)
      }
    }
    private def putTree(tree: Tree) {
      if (putEntry(tree))
        putTreeTraverser put tree
    }

    /** Store a constant in map index, along with anything it references.
     */
    private def putConstant(c: Constant) {
      if (putEntry(c)) {
        if (c.tag == StringTag) putEntry(newTermName(c.stringValue))
        else if (c.tag == ClazzTag) putType(c.typeValue)
        else if (c.tag == EnumTag) putSymbol(c.symbolValue)
      }
    }

    private def putChildren(sym: Symbol, children: List[Symbol]) {
      putEntry(sym -> children)
      children foreach putSymbol
    }

    /** used in putSymbol only, i.e. annotations on definitions, not on types */
    private def putAnnotation(sym: Symbol, annot: AnnotationInfo) {
      // if an annotation with the same arguments is applied to the
      // same symbol multiple times, it's only pickled once.
      if (putEntry(sym -> annot))
        putAnnotationBody(annot)
    }

    private def putAnnotation(annot: AnnotationInfo) {
      if (putEntry(annot))
        putAnnotationBody(annot)
    }

    /** Puts the members of an AnnotationInfo */
    private def putAnnotationBody(annot: AnnotationInfo) {
      def putAnnotArg(arg: Tree) {
        arg match {
          case Literal(c) => putConstant(c)
          case _ => putTree(arg)
        }
      }
      def putClassfileAnnotArg(carg: ClassfileAnnotArg) {
        (carg: @unchecked) match {
          case LiteralAnnotArg(const)  => putConstant(const)
          case ArrayAnnotArg(args)     => if (putEntry(carg)) args foreach putClassfileAnnotArg
          case NestedAnnotArg(annInfo) => putAnnotation(annInfo)
        }
      }
      val AnnotationInfo(tpe, args, assocs) = annot
      putType(tpe)
      args foreach putAnnotArg
      assocs foreach { asc =>
        putEntry(asc._1)
        putClassfileAnnotArg(asc._2)
      }
    }

    // Phase 2 methods: Write all entries to byte array ------------------------------

    /** Write a reference to object, i.e., the object's number in the map index.
     */
    private def writeRef(ref: AnyRef) {
      writeNat(index(deskolemizeTypeSymbols(ref)))
    }
    private def writeRefs(refs: List[AnyRef]): Unit = refs foreach writeRef

    private def writeRefsWithLength(refs: List[AnyRef]) {
      writeNat(refs.length)
      writeRefs(refs)
    }

    /** Write name, owner, flags, and info of a symbol.
     */
    private def writeSymInfo(sym: Symbol) {
      writeRef(sym.name)
      writeRef(localizedOwner(sym))
      writeLongNat((rawToPickledFlags(sym.rawflags & PickledFlags)))
      if (sym.hasAccessBoundary) writeRef(sym.privateWithin)
      writeRef(sym.info)
    }

    /** Write a name in UTF8 format. */
    private def writeName(name: Name) {
      ensureCapacity(name.length * 3)
      val utfBytes = Codec toUTF8 name.toString
      scala.compat.Platform.arraycopy(utfBytes, 0, bytes, writeIndex, utfBytes.length)
      writeIndex += utfBytes.length
    }

    /** Write an annotation */
    private def writeAnnotation(annot: AnnotationInfo) {
      def writeAnnotArg(arg: Tree) {
        arg match {
          case Literal(c) => writeRef(c)
          case _ => writeRef(arg)
        }
      }

      writeRef(annot.atp)
      annot.args foreach writeAnnotArg
      annot.assocs foreach { asc =>
        writeRef(asc._1)
        writeClassfileAnnotArg(asc._2)
      }
    }

    /** Write a ClassfileAnnotArg (argument to classfile annotation) */
    def writeClassfileAnnotArg(carg: ClassfileAnnotArg) {
      (carg: @unchecked) match {
        case LiteralAnnotArg(const)  => writeRef(const)
        case ArrayAnnotArg(args)     => writeRef(carg)
        case NestedAnnotArg(annInfo) => writeRef(annInfo)
      }
    }

    private object writeTreeBodyTraverser extends Traverser {
      private var refs = false
      @inline private def asRefs[T](body: => T): T = {
        val saved = refs
        refs = true
        try body finally refs = saved
      }
      override def traverseModifiers(mods: Modifiers): Unit          = if (refs) writeRef(mods) else super.traverseModifiers(mods)
      override def traverseName(name: Name): Unit                    = writeRef(name)
      override def traverseConstant(const: Constant): Unit           = writeRef(const)
      override def traverseParams(params: List[Tree]): Unit          = writeRefsWithLength(params)
      override def traverseParamss(vparamss: List[List[Tree]]): Unit = {
        writeNat(vparamss.length)
        super.traverseParamss(vparamss)
      }
      override def traverse(tree: Tree): Unit = {
        if (refs)
          writeRef(tree)
        else {
          writeRef(tree.tpe)
          if (tree.hasSymbolField)
            writeRef(tree.symbol)

          asRefs(super.traverse(tree))
        }
      }
    }

    /** Write an entry */
    private def writeEntry(entry: AnyRef) {
      def writeLocalSymbolBody(sym: Symbol) {
        writeSymInfo(sym)
        sym match {
          case _: ClassSymbol if sym.hasSelfType => writeRef(sym.typeOfThis)
          case _: TermSymbol if sym.alias.exists => writeRef(sym.alias)
          case _                                 =>
        }
      }
      def writeExtSymbolBody(sym: Symbol) {
        val name = if (sym.isModuleClass) sym.name.toTermName else sym.name
        writeRef(name)
        if (!sym.owner.isRoot)
          writeRef(sym.owner)
      }
      def writeSymbolBody(sym: Symbol) {
        if (sym ne NoSymbol) {
          if (isLocalToPickle(sym))
            writeLocalSymbolBody(sym)
          else
            writeExtSymbolBody(sym)
        }
      }

      // NullaryMethodType reuses POLYtpe since those can never have an empty list of tparams.
      // TODO: is there any way this can come back and bite us in the bottom?
      // ugliness and thrift aside, this should make this somewhat more backward compatible
      // (I'm not sure how old scalac's would deal with nested PolyTypes, as these used to be folded into one)
      def writeTypeBody(tpe: Type): Unit = tpe match {
        case NoType | NoPrefix                   =>
        case ThisType(sym)                       => writeRef(sym)
        case SingleType(pre, sym)                => writeRef(pre) ; writeRef(sym)
        case SuperType(thistpe, supertpe)        => writeRef(thistpe) ; writeRef(supertpe)
        case ConstantType(value)                 => writeRef(value)
        case TypeBounds(lo, hi)                  => writeRef(lo) ; writeRef(hi)
        case TypeRef(pre, sym, args)             => writeRef(pre) ; writeRef(sym); writeRefs(args)
        case MethodType(formals, restpe)         => writeRef(restpe) ; writeRefs(formals)
        case NullaryMethodType(restpe)           => writeRef(restpe); writeRefs(Nil)
        case PolyType(tparams, restpe)           => writeRef(restpe); writeRefs(tparams)
        case ExistentialType(tparams, restpe)    => writeRef(restpe); writeRefs(tparams)
        case StaticallyAnnotatedType(annots, tp) => writeRef(tp) ; writeRefs(annots)
        case AnnotatedType(_, tp)                => writeTypeBody(tp) // write the underlying type if there are no static annotations
        case CompoundType(parents, _, clazz)     => writeRef(clazz); writeRefs(parents)
      }

      def writeTreeBody(tree: Tree) {
        writeNat(picklerSubTag(tree))
        if (!tree.isEmpty)
          writeTreeBodyTraverser traverse tree
      }

      def writeConstant(c: Constant): Unit = c.tag match {
        case BooleanTag => writeLong(if (c.booleanValue) 1 else 0)
        case FloatTag   => writeLong(floatToIntBits(c.floatValue).toLong)
        case DoubleTag  => writeLong(doubleToLongBits(c.doubleValue))
        case StringTag  => writeRef(newTermName(c.stringValue))
        case ClazzTag   => writeRef(c.typeValue)
        case EnumTag    => writeRef(c.symbolValue)
        case tag        => if (ByteTag <= tag && tag <= LongTag) writeLong(c.longValue)
      }

      def writeModifiers(mods: Modifiers) {
        val pflags = rawToPickledFlags(mods.flags)
        writeNat((pflags >> 32).toInt)
        writeNat((pflags & 0xFFFFFFFF).toInt)
        writeRef(mods.privateWithin)
      }

      def writeSymbolTuple(target: Symbol, other: Any) {
        writeRef(target)
        other match {
          case annot: AnnotationInfo             => writeAnnotation(annot)
          case children: List[Symbol @unchecked] => writeRefs(children)
          case _                                 =>
        }
      }

      def writeBody(entry: AnyRef): Unit = entry match {
        case tree: Tree              => writeTreeBody(tree)
        case sym: Symbol             => writeSymbolBody(sym)
        case tpe: Type               => writeTypeBody(tpe)
        case name: Name              => writeName(name)
        case const: Constant         => writeConstant(const)
        case mods: Modifiers         => writeModifiers(mods)
        case annot: AnnotationInfo   => writeAnnotation(annot)
        case (target: Symbol, other) => writeSymbolTuple(target, other)
        case ArrayAnnotArg(args)     => args foreach writeClassfileAnnotArg
        case _                       => devWarning(s"Unexpected entry to pickler ${shortClassOfInstance(entry)} $entry")
      }

      // begin writeEntry
      // The picklerTag method can't determine if it's an external symbol reference
      val tag = entry match {
        case sym: Symbol if isExternalSymbol(sym) => if (sym.isModuleClass) EXTMODCLASSref else EXTref
        case _                                    => picklerTag(entry)
      }
      writeNat(tag)
      writeByte(0) // reserve a place to record the number of bytes written
      val start = writeIndex
      writeBody(entry)
      val length = writeIndex - start
      patchNat(start - 1, length) // patch bytes written over the placeholder
    }

    /** Write byte array */
    def writeArray() {
      assert(writeIndex == 0)
      writeNat(MajorVersion)
      writeNat(MinorVersion)
      writeNat(ep)

      entries take ep foreach writeEntry
    }

    override def toString = "" + rootName + " in " + rootOwner
  }
}

Other Scala source code examples

Here is a short list of links related to this Scala Pickler.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.