/* 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", ""
)
)
}
}
}