summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
blob: 353b1ffd8944fb2e8688d97dfd7e4759a6d98bca (plain) (tree)
































































































































                                                                                                         
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools.nsc
package symtab

/** Printing the symbol graph (for those symbols attached to an AST node)
 *  after each phase.
 */
trait SymbolTrackers {
  val global: Global
  import global._

  private implicit lazy val SymbolOrdering: Ordering[Symbol] =
    Ordering by (x => (x.kindString, x.name.toString))

  private implicit def toList[T: Ordering](xs: Set[T]): List[T] = xs.toList.sorted

  /** Reversing the direction of Symbol's owner arrow. */
  trait Hierarchy {
    def root: Symbol
    def children: List[Hierarchy]
    def flatten: Set[Symbol]
    def indentString(indent: String): String
    def symString(sym: Symbol): String

    override def toString() = indentString("")
  }
  case class Change(
    added: Set[Symbol],
    removed: Set[Symbol],
    changedOwner: Map[Symbol, Symbol]
  )

  object SymbolTracker {
    def containsSymbol(t: Tree) = t.symbol != null && t.symbol != NoSymbol

    def symbolsInUnit(unit: CompilationUnit): Set[Symbol] = {
      if (unit.body == null) Set.empty[Symbol]
      else unit.body filter containsSymbol map (_.symbol) toSet
    }
    def apply(unit: CompilationUnit) = new SymbolTracker(
      () => symbolsInUnit(unit) filterNot (_.ownerChain.exists(_ hasFlag Flags.SPECIALIZED))
    )
  }

  class SymbolTracker(sourceFn: () => Set[Symbol]) {
    private var history    = List[Change]()
    private var prev       = Set[Symbol]()
    private var prevOwners = Map[Symbol, Symbol]()
    private def changedOwner(sym: Symbol) = prevOwners get sym filter (_ != sym.owner)
    private def changedOwnerString(sym: Symbol) = changedOwner(sym) match {
      case Some(prev) => "[Owner changed: was " + ownersString(sym, 2) + "]"
      case _          => ""
    }
    private implicit def NodeOrdering: Ordering[Node] = Ordering by (_.root)
    private def ownersString(sym: Symbol, num: Int) = sym.ownerChain drop 1 take num mkString " -> "
    private def allOwnersString(sym: Symbol) = sym.ownerChain mkString " -> "
    private def isAdded(sym: Symbol) = history.nonEmpty && history.head.added(sym)
    private def isOwnerChange(sym: Symbol) = history.nonEmpty && (history.head.changedOwner contains sym)

    object Node {
      def nodes(syms: Set[Symbol]): List[Node] = {
        def descendents(s: Symbol) = (syms - s) filter (_ hasTransOwner s)
        def rooted(root: Symbol)   = new Node(root, nodes(descendents(root)))

        val roots    = syms filterNot (_.ownerChain drop 1 exists syms)
        val deep     = roots map rooted
        val deepSyms = deep flatMap (_.flatten)

        deep ++ (syms filterNot deepSyms map (x => Node(x)))
      }

      def apply(sym: Symbol): Node = new Node(sym, Nil)
      def apply(syms: Set[Symbol]): Node = nodes(syms) match {
        case List(x)  => x
        case xs       => new Node(NoSymbol, xs)
      }
    }
    class Node(val root: Symbol, val children: List[Hierarchy]) extends Hierarchy {
      def indicatorString = if (isAdded(root)) "* " else "  "

      def symString(sym: Symbol) = (
        sym + changedOwnerString(sym) + " " +
        sym.hasFlagsToString(Flags.PrintableFlags)
      )

      def flatten = children.foldLeft(Set(root))(_ ++ _.flatten)
      def indentString(indent: String): String = {
        if (root == NoSymbol)
          children map (c => c.indentString(indent)) mkString "\n"
        else {
          indicatorString + indent + symString(root) + (
            if (children.isEmpty) ""
            else children map (c => c.indentString(indent + "    ")) mkString ("\n", "\n", "")
          )
        }
      }
    }

    def snapshot(): Unit = {
      val syms    = sourceFn()
      val added   = syms filterNot prev
      val removed = prev filterNot syms
      val changed = ({
        for (sym <- prev intersect syms; old <- changedOwner(sym)) yield
          (sym, old)
      }).toMap

      val change = Change(added, removed, changed)
      prev       = syms
      prevOwners = syms map (s => (s, s.owner)) toMap;
      history    = change :: history
    }
    def show(): String = {
      val hierarchy = Node(sourceFn())
      val removed   = if (history.isEmpty) Set() else history.head.removed

      hierarchy.toString() + (
        if (removed.isEmpty) ""
        else removed map allOwnersString mkString (
          "\n\n!!! " + removed.size + " symbols vanished:\n", "\n", ""
        )
      )
    }
  }
}