summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
blob: aaa53c284ae4bd998649d4a893bc2e8170fc79fd (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],
    owners: Map[Symbol, Symbol],  // symbol -> previous owner
    flags: Map[Symbol, Long]      // symbol -> previous flags
  )

  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]) {
    def flagsMask: Long = Flags.PrintableFlags

    private var current    = Set[Symbol]()
    private var history    = List[Change](Change(Set(), Set(), Map(), Map()))
    private var prev       = Set[Symbol]()
    private var prevFlags  = Map[Symbol, Long]()
    private var prevOwners = Map[Symbol, Symbol]()

    private def changed                    = history.head
    private def isAdded(sym: Symbol)       = changed added sym
    private def isOwnerChange(sym: Symbol) = changed.owners contains sym
    private def isFlagsChange(sym: Symbol) = changed.flags contains sym

    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 " -> "

    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 masked = root.flags & flagsMask
      def indicatorString =
        if (isAdded(root)) "* "
        else List(
          if (isFlagsChange(root)) "F" else "",
          if (isOwnerChange(root)) "O" else "",
          "  "
        ).mkString take 2

      def changedOwnerString = changed.owners get root match {
        case Some(prev) => " [Owner was " + prev + ", now " + root.owner + "]"
        case _          => ""
      }
      def flagSummaryString = changed.flags get root match {
        case Some(oldFlags) =>
          val added   = masked & ~oldFlags
          val removed = oldFlags & ~masked
          val steady  = masked & ~(added | removed)
          val all     = masked | oldFlags
          val strs    = 0 to 63 map { bit =>
            val flag = 1L << bit
            val prefix = (
              if ((added & flag) != 0L) "+"
              else if ((removed & flag) != 0L) "-"
              else ""
            )
            if ((all & flag) == 0L) ""
            else prefix + Flags.flagToString(flag)
          }

          " " + strs.filterNot(_ == "").mkString("[", " ", "]")
        case _ =>
          if (masked == 0L) ""
          else " " + Flags.flagsToString(masked)
      }
      def symString(sym: Symbol) = (
        sym + changedOwnerString + flagSummaryString
      )

      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 = {
      current = sourceFn()

      val added   = current filterNot prev
      val removed = prev filterNot current
      val steady  = prev intersect current

      def changedOwner(sym: Symbol) = prevOwners get sym filter (_ != sym.owner)
      def changedFlags(sym: Symbol) = prevFlags get sym filter (_ != (sym.flags & flagsMask))

      val owners = ({
        for (sym <- steady; old <- changedOwner(sym)) yield
          (sym, old)
      }).toMap
      val flags = ({
        for (sym <- steady; old <- changedFlags(sym)) yield
          (sym, old)
      }).toMap

      val change = Change(added, removed, owners, flags)

      prev       = current
      prevOwners = current map (s => (s, s.owner)) toMap;
      prevFlags  = current map (s => (s, (s.flags & flagsMask))) toMap;
      history    = change :: history
    }
    def show(): String = {
      val hierarchy = Node(current)
      val removed   = history.head.removed

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